package main import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestDecodeBindingsStreamStreamsTripleBindings(t *testing.T) { payload := `{ "head": {"vars": ["s", "p", "o"]}, "results": { "bindings": [ { "s": {"type": "uri", "value": "http://example.com/s1"}, "p": {"type": "uri", "value": "http://example.com/p"}, "o": {"type": "uri", "value": "http://example.com/o1"} }, { "s": {"type": "uri", "value": "http://example.com/s2"}, "p": {"type": "uri", "value": "http://example.com/p"}, "o": {"type": "uri", "value": "http://example.com/o2"} } ] } }` var got []sparqlTripleBinding count, err := decodeBindingsStream(json.NewDecoder(strings.NewReader(payload)), func(binding sparqlTripleBinding) error { got = append(got, binding) return nil }) if err != nil { t.Fatalf("decodeBindingsStream returned error: %v", err) } if count != 2 { t.Fatalf("expected 2 bindings, got %d", count) } if len(got) != 2 { t.Fatalf("expected 2 streamed bindings, got %d", len(got)) } if got[0].S.Value != "http://example.com/s1" || got[1].O.Value != "http://example.com/o2" { t.Fatalf("unexpected streamed bindings: %+v", got) } } func TestQueryJSONDecodesTypedBindings(t *testing.T) { t.Run("predicates", func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/sparql-results+json") _, _ = w.Write([]byte(`{ "head": {"vars": ["p"]}, "results": { "bindings": [ {"p": {"type": "uri", "value": "http://example.com/p"}} ] } }`)) })) defer server.Close() client := &AnzoGraphClient{ cfg: Config{SparqlTimeout: 5 * time.Second}, endpoint: server.URL, client: server.Client(), } var res sparqlBindingsResponse[sparqlPredicateBinding] metrics, err := client.QueryJSON(context.Background(), "SELECT ?p WHERE { ?s ?p ?o }", &res) if err != nil { t.Fatalf("QueryJSON returned error: %v", err) } if len(res.Results.Bindings) != 1 || res.Results.Bindings[0].P.Value != "http://example.com/p" { t.Fatalf("unexpected predicate bindings: %+v", res.Results.Bindings) } if metrics.ResponseBytes == 0 { t.Fatalf("expected QueryJSON to record response bytes") } })) t.Run("labels", func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/sparql-results+json") _, _ = w.Write([]byte(`{ "head": {"vars": ["s", "label"]}, "results": { "bindings": [ { "s": {"type": "uri", "value": "http://example.com/s"}, "label": {"type": "literal", "xml:lang": "en", "value": "Example"} } ] } }`)) })) defer server.Close() client := &AnzoGraphClient{ cfg: Config{SparqlTimeout: 5 * time.Second}, endpoint: server.URL, client: server.Client(), } var res sparqlBindingsResponse[sparqlLabelBinding] _, err := client.QueryJSON(context.Background(), "SELECT ?s ?label WHERE { ?s ?p ?label }", &res) if err != nil { t.Fatalf("QueryJSON returned error: %v", err) } if len(res.Results.Bindings) != 1 { t.Fatalf("expected 1 label binding, got %d", len(res.Results.Bindings)) } if res.Results.Bindings[0].Label.Value != "Example" || res.Results.Bindings[0].Label.Lang != "en" { t.Fatalf("unexpected label binding: %+v", res.Results.Bindings[0]) } })) } func TestQueryTripleBindingsStreamReportsTruncatedJSON(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/sparql-results+json") _, _ = w.Write([]byte(`{"results":{"bindings":[{"s":{"type":"uri","value":"http://example.com/s"}`)) })) defer server.Close() client := &AnzoGraphClient{ cfg: Config{SparqlTimeout: 5 * time.Second}, endpoint: server.URL, client: server.Client(), } _, err := client.QueryTripleBindingsStream(context.Background(), "SELECT ?s ?p ?o WHERE { ?s ?p ?o }", func(binding sparqlTripleBinding) error { return nil }) if err == nil { t.Fatalf("expected truncated JSON error") } if !strings.Contains(err.Error(), "truncated SPARQL JSON") { t.Fatalf("expected truncated JSON error, got %v", err) } }