package research_client_test import ( "bytes" "context" "crypto/sha256" "encoding/hex" "os" "path/filepath" "reflect" "regexp" "runtime" "strings" "testing" "code.tnxs.net/vernonkeenan/lib/api/research/research_client/factors" "code.tnxs.net/vernonkeenan/lib/api/research/research_client/financial_statements" "code.tnxs.net/vernonkeenan/lib/api/research/research_client/observations" "code.tnxs.net/vernonkeenan/lib/api/research/research_client/topics" openapiruntime "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) const researchV0716SpecSHA256 = "e7739ece74245709a8af8eab7bcffdbf1ab3e1be5763bc71a4c633b699d4df14" type insightCaptureTransport struct { operation *openapiruntime.ClientOperation } func (transport *insightCaptureTransport) Submit(operation *openapiruntime.ClientOperation) (any, error) { return transport.SubmitContext(context.Background(), operation) } func (transport *insightCaptureTransport) SubmitContext(_ context.Context, operation *openapiruntime.ClientOperation) (any, error) { transport.operation = operation switch operation.ID { case "getTopics": return topics.NewGetTopicsOK(), nil case "postTopics": return topics.NewPostTopicsOK(), nil case "putTopic": return topics.NewPutTopicOK(), nil case "getFactors": return factors.NewGetFactorsOK(), nil case "postFactors": return factors.NewPostFactorsOK(), nil case "putFactor": return factors.NewPutFactorOK(), nil case "getFinancialStatements": return financial_statements.NewGetFinancialStatementsOK(), nil case "postFinancialStatements": return financial_statements.NewPostFinancialStatementsOK(), nil case "putFinancialStatement": return financial_statements.NewPutFinancialStatementOK(), nil case "getObservations": return observations.NewGetObservationsOK(), nil case "postObservations": return observations.NewPostObservationsOK(), nil case "putObservation": return observations.NewPutObservationOK(), nil default: panic("unexpected generated insight operation: " + operation.ID) } } func TestGeneratedInsightOperationContract(t *testing.T) { type operationCall func(openapiruntime.ContextualTransport) error tests := []struct { name string wantID string wantMethod string wantPath string call operationCall }{ { name: "list topics", wantID: "getTopics", wantMethod: "GET", wantPath: "/topics", call: func(transport openapiruntime.ContextualTransport) error { _, err := topics.New(transport, strfmt.Default).GetTopics(topics.NewGetTopicsParams(), nil) return err }, }, { name: "create topic", wantID: "postTopics", wantMethod: "POST", wantPath: "/topics", call: func(transport openapiruntime.ContextualTransport) error { _, err := topics.New(transport, strfmt.Default).PostTopics(topics.NewPostTopicsParams(), nil) return err }, }, { name: "update topic", wantID: "putTopic", wantMethod: "PUT", wantPath: "/topics", call: func(transport openapiruntime.ContextualTransport) error { _, err := topics.New(transport, strfmt.Default).PutTopic(topics.NewPutTopicParams(), nil) return err }, }, { name: "list factors", wantID: "getFactors", wantMethod: "GET", wantPath: "/factors", call: func(transport openapiruntime.ContextualTransport) error { _, err := factors.New(transport, strfmt.Default).GetFactors(factors.NewGetFactorsParams(), nil) return err }, }, { name: "create factor", wantID: "postFactors", wantMethod: "POST", wantPath: "/factors", call: func(transport openapiruntime.ContextualTransport) error { _, err := factors.New(transport, strfmt.Default).PostFactors(factors.NewPostFactorsParams(), nil) return err }, }, { name: "update factor", wantID: "putFactor", wantMethod: "PUT", wantPath: "/factors", call: func(transport openapiruntime.ContextualTransport) error { _, err := factors.New(transport, strfmt.Default).PutFactor(factors.NewPutFactorParams(), nil) return err }, }, { name: "list financial statements", wantID: "getFinancialStatements", wantMethod: "GET", wantPath: "/financialstatements", call: func(transport openapiruntime.ContextualTransport) error { _, err := financial_statements.New(transport, strfmt.Default).GetFinancialStatements(financial_statements.NewGetFinancialStatementsParams(), nil) return err }, }, { name: "create financial statement", wantID: "postFinancialStatements", wantMethod: "POST", wantPath: "/financialstatements", call: func(transport openapiruntime.ContextualTransport) error { _, err := financial_statements.New(transport, strfmt.Default).PostFinancialStatements(financial_statements.NewPostFinancialStatementsParams(), nil) return err }, }, { name: "update financial statement", wantID: "putFinancialStatement", wantMethod: "PUT", wantPath: "/financialstatements", call: func(transport openapiruntime.ContextualTransport) error { _, err := financial_statements.New(transport, strfmt.Default).PutFinancialStatement(financial_statements.NewPutFinancialStatementParams(), nil) return err }, }, { name: "list observations", wantID: "getObservations", wantMethod: "GET", wantPath: "/observations", call: func(transport openapiruntime.ContextualTransport) error { _, err := observations.New(transport, strfmt.Default).GetObservations(observations.NewGetObservationsParams(), nil) return err }, }, { name: "create observation", wantID: "postObservations", wantMethod: "POST", wantPath: "/observations", call: func(transport openapiruntime.ContextualTransport) error { _, err := observations.New(transport, strfmt.Default).PostObservations(observations.NewPostObservationsParams(), nil) return err }, }, { name: "update observation", wantID: "putObservation", wantMethod: "PUT", wantPath: "/observations", call: func(transport openapiruntime.ContextualTransport) error { _, err := observations.New(transport, strfmt.Default).PutObservation(observations.NewPutObservationParams(), nil) return err }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { transport := &insightCaptureTransport{} if err := test.call(transport); err != nil { t.Fatalf("generated client operation failed: %v", err) } if transport.operation == nil { t.Fatal("generated client did not submit an operation") } if transport.operation.ID != test.wantID { t.Errorf("operation ID = %q, want %q", transport.operation.ID, test.wantID) } if transport.operation.Method != test.wantMethod { t.Errorf("method = %q, want %q", transport.operation.Method, test.wantMethod) } if transport.operation.PathPattern != test.wantPath { t.Errorf("path = %q, want %q", transport.operation.PathPattern, test.wantPath) } }) } } func TestGeneratedInsightGetByIDFiltersAndConflictResponses(t *testing.T) { recordID := "record-id" if got := topics.NewGetTopicsParams().WithTopicID(&recordID).TopicID; got == nil || *got != recordID { t.Fatal("Topic get-by-ID filter is absent") } if got := factors.NewGetFactorsParams().WithFactorID(&recordID).FactorID; got == nil || *got != recordID { t.Fatal("Factor get-by-ID filter is absent") } if got := financial_statements.NewGetFinancialStatementsParams().WithFinancialStatementID(&recordID).FinancialStatementID; got == nil || *got != recordID { t.Fatal("FinancialStatement get-by-ID filter is absent") } if got := observations.NewGetObservationsParams().WithObservationID(&recordID).ObservationID; got == nil || *got != recordID { t.Fatal("Observation get-by-ID filter is absent") } conflicts := []interface{ IsCode(int) bool }{ topics.NewPutTopicConflict(), factors.NewPutFactorConflict(), financial_statements.NewPutFinancialStatementConflict(), observations.NewPutObservationConflict(), } for _, conflict := range conflicts { if !conflict.IsCode(409) { t.Errorf("%T does not preserve the optimistic-conflict response", conflict) } } } func TestGeneratedInsightSurfaceDoesNotInventUnsupportedOperations(t *testing.T) { contracts := []reflect.Type{ reflect.TypeOf((*topics.ClientService)(nil)).Elem(), reflect.TypeOf((*factors.ClientService)(nil)).Elem(), reflect.TypeOf((*financial_statements.ClientService)(nil)).Elem(), reflect.TypeOf((*observations.ClientService)(nil)).Elem(), } for _, contract := range contracts { for index := 0; index < contract.NumMethod(); index++ { method := contract.Method(index).Name if strings.Contains(strings.ToLower(method), "bulk") { t.Errorf("%s invented unsupported bulk operation %s", contract, method) } } } topicContract := reflect.TypeOf((*topics.ClientService)(nil)).Elem() for _, method := range []string{"DeleteTopic", "DeleteTopicContext", "DeleteTopics", "DeleteTopicsContext"} { if _, exists := topicContract.MethodByName(method); exists { t.Errorf("Topic client invented unsupported operation %s", method) } } } func TestResearchSpecIsPinnedToDeployedV0716Contract(t *testing.T) { repoRoot := insightRepoRoot(t) mainSpec, err := os.ReadFile(filepath.Join(repoRoot, "swagger", "research-vernonkeenan.yaml")) if err != nil { t.Fatalf("read Research spec: %v", err) } sum := sha256.Sum256(mainSpec) if got := hex.EncodeToString(sum[:]); got != researchV0716SpecSHA256 { t.Fatalf("Research spec drifted from deployed v0.7.16: SHA-256 = %s, want %s", got, researchV0716SpecSHA256) } externalSpec, err := os.ReadFile(filepath.Join(repoRoot, "swagger", "external", "research-vernonkeenan.yaml")) if err != nil { t.Fatalf("read external Research spec: %v", err) } normalized := bytes.ReplaceAll(externalSpec, []byte(`"https"`), []byte(`"http"`)) normalized = bytes.ReplaceAll(normalized, []byte("gw.tnxs.net"), []byte("research.vernonkeenan.com:8080")) normalized = bytes.ReplaceAll(normalized, []byte(`"/vk/research/v1"`), []byte(`"/v1"`)) if !bytes.Equal(normalized, mainSpec) { t.Fatal("external Research spec differs from the conventional host/scheme/base-path rewrite") } } func TestGeneratedInsightSurfaceHasNoSalesforceOrEmbeddedSecrets(t *testing.T) { repoRoot := insightRepoRoot(t) clientRoot := filepath.Join(repoRoot, "api", "research", "research_client") modelRoot := filepath.Join(repoRoot, "api", "research", "research_models") targets := []string{ filepath.Join(clientRoot, "topics"), filepath.Join(clientRoot, "factors"), filepath.Join(clientRoot, "financial_statements"), filepath.Join(clientRoot, "observations"), filepath.Join(modelRoot, "topic.go"), filepath.Join(modelRoot, "topic_request.go"), filepath.Join(modelRoot, "topic_response.go"), filepath.Join(modelRoot, "factor.go"), filepath.Join(modelRoot, "factor_request.go"), filepath.Join(modelRoot, "factor_response.go"), filepath.Join(modelRoot, "financial_statement.go"), filepath.Join(modelRoot, "financial_statement_request.go"), filepath.Join(modelRoot, "financial_statement_response.go"), filepath.Join(modelRoot, "observation.go"), filepath.Join(modelRoot, "observation_request.go"), filepath.Join(modelRoot, "observation_response.go"), } credentialLiteral := regexp.MustCompile(`(?i)(api[_-]?key|password|secret)\s*[:=]\s*["'][^"']+["']`) for _, target := range targets { err := filepath.WalkDir(target, func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if entry.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { return nil } content, err := os.ReadFile(path) if err != nil { return err } lower := strings.ToLower(string(content)) for _, forbidden := range []string{"salesforce", "sf-gate", "go-force", "private key-----"} { if strings.Contains(lower, forbidden) { t.Errorf("%s contains forbidden boundary %q", path, forbidden) } } if credentialLiteral.Match(content) { t.Errorf("%s contains a credential-shaped literal", path) } return nil }) if err != nil { t.Fatalf("scan generated contract target %s: %v", target, err) } } } func insightRepoRoot(t *testing.T) string { t.Helper() _, testFile, _, ok := runtime.Caller(0) if !ok { t.Fatal("resolve test source path") } return filepath.Clean(filepath.Join(filepath.Dir(testFile), "..", "..", "..")) }