package unraid import ( "context" "encoding/json" "encoding/pem" "net/http" "net/http/httptest" "strings" "testing" ) func TestClientAllowsOnlyReadOperationsAndUsesUnraidAPIKey(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost || r.Header.Get("x-api-key") != "test-token" || r.Header.Get("Authorization") != "" { t.Fatalf("unexpected request") } var request struct { Query string `json:"query"` OperationName string `json:"operationName"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { t.Fatal(err) } if request.OperationName != "server" || !strings.HasPrefix(request.Query, "query server ") { t.Fatalf("query and operation name must agree: %#v", request) } _, _ = w.Write([]byte(`{"data":{"server":{"name":"pulse","status":"ONLINE"}}}`)) })) defer server.Close() c, err := New(server.URL, "test-token", server.Client()) if err != nil { t.Fatal(err) } if _, err := c.Query(context.Background(), "server"); err != nil { t.Fatal(err) } if _, err := c.Query(context.Background(), "deleteContainer"); err == nil || !strings.Contains(err.Error(), "read-only") { t.Fatalf("unexpected mutation result: %v", err) } } func TestClientUsesOnlyDocumentedReadOnlySelections(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var request struct { Query string `json:"query"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { t.Fatal(err) } if !strings.Contains(request.Query, "docker { containers {") || strings.Contains(strings.ToLower(request.Query), "mutation") { t.Fatalf("unexpected query %q", request.Query) } _, _ = w.Write([]byte(`{"data":{"docker":{"containers":[]}}}`)) })) defer server.Close() client, err := New(server.URL, "test-token", server.Client()) if err != nil { t.Fatal(err) } if _, err := client.Query(context.Background(), "containers"); err != nil { t.Fatal(err) } } func TestRejectsNonHTTPSEndpoint(t *testing.T) { if _, err := New("http://unraid.local/graphql", "token", nil); err == nil { t.Fatal("expected HTTPS requirement") } } func TestClientTrustsExplicitCAPEMWithoutDisablingVerification(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"data":{"server":{"name":"Tower","status":"ONLINE"}}}`)) })) defer server.Close() certificate := server.Certificate() caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw}) client, err := NewWithCAPEM(server.URL, "viewer-key", caPEM) if err != nil { t.Fatal(err) } if _, err := client.Query(context.Background(), "server"); err != nil { t.Fatal(err) } if _, err := NewWithCAPEM(server.URL, "viewer-key", []byte("not a certificate")); err == nil { t.Fatal("expected invalid private CA rejection") } }