package members_client_test import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "os" "path/filepath" "reflect" "regexp" "runtime" "strings" "testing" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/prompts" "code.tnxs.net/vernonkeenan/lib/api/members/members_models" openapiruntime "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) const membersPromptProviderSpecSHA256 = "ea2c06f8551555ecf3d36e4d728ba34beeae8731370a64535e194686b16ad07f" var promptTestAuth = openapiruntime.ClientAuthInfoWriterFunc(func(openapiruntime.ClientRequest, strfmt.Registry) error { return nil }) type promptCaptureTransport struct { operation *openapiruntime.ClientOperation } func (transport *promptCaptureTransport) Submit(operation *openapiruntime.ClientOperation) (any, error) { return transport.SubmitContext(context.Background(), operation) } func (transport *promptCaptureTransport) SubmitContext(_ context.Context, operation *openapiruntime.ClientOperation) (any, error) { transport.operation = operation switch operation.ID { case "getPrompts": return prompts.NewGetPromptsOK(), nil case "postPrompts": return prompts.NewPostPromptsOK(), nil case "putPrompts": return prompts.NewPutPromptsOK(), nil case "getPromptAnswers": return prompts.NewGetPromptAnswersOK(), nil case "postPromptAnswers": return prompts.NewPostPromptAnswersOK(), nil case "getPromptCategories": return prompts.NewGetPromptCategoriesOK(), nil case "postPromptCategories": return prompts.NewPostPromptCategoriesOK(), nil case "putPromptCategories": return prompts.NewPutPromptCategoriesOK(), nil case "getPromptTags": return prompts.NewGetPromptTagsOK(), nil case "postPromptTags": return prompts.NewPostPromptTagsOK(), nil case "putPromptTags": return prompts.NewPutPromptTagsOK(), nil default: panic("unexpected generated prompt operation: " + operation.ID) } } func TestGeneratedPromptOperationContract(t *testing.T) { type operationCall func(openapiruntime.ContextualTransport) error tests := []struct { name string wantID string wantMethod string wantPath string call operationCall }{ { name: "list prompts", wantID: "getPrompts", wantMethod: "GET", wantPath: "/prompts", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).GetPrompts(prompts.NewGetPromptsParams(), promptTestAuth) return err }, }, { name: "create prompt", wantID: "postPrompts", wantMethod: "POST", wantPath: "/prompts", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PostPrompts(prompts.NewPostPromptsParams(), promptTestAuth) return err }, }, { name: "update prompt", wantID: "putPrompts", wantMethod: "PUT", wantPath: "/prompts", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PutPrompts(prompts.NewPutPromptsParams(), promptTestAuth) return err }, }, { name: "list prompt answers", wantID: "getPromptAnswers", wantMethod: "GET", wantPath: "/promptanswers", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).GetPromptAnswers(prompts.NewGetPromptAnswersParams(), promptTestAuth) return err }, }, { name: "append prompt answer", wantID: "postPromptAnswers", wantMethod: "POST", wantPath: "/promptanswers", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PostPromptAnswers(prompts.NewPostPromptAnswersParams(), promptTestAuth) return err }, }, { name: "list prompt categories", wantID: "getPromptCategories", wantMethod: "GET", wantPath: "/promptcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).GetPromptCategories(prompts.NewGetPromptCategoriesParams(), promptTestAuth) return err }, }, { name: "create prompt category", wantID: "postPromptCategories", wantMethod: "POST", wantPath: "/promptcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PostPromptCategories(prompts.NewPostPromptCategoriesParams(), promptTestAuth) return err }, }, { name: "update prompt category", wantID: "putPromptCategories", wantMethod: "PUT", wantPath: "/promptcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PutPromptCategories(prompts.NewPutPromptCategoriesParams(), promptTestAuth) return err }, }, { name: "list prompt tags", wantID: "getPromptTags", wantMethod: "GET", wantPath: "/prompttags", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).GetPromptTags(prompts.NewGetPromptTagsParams(), promptTestAuth) return err }, }, { name: "create prompt tag", wantID: "postPromptTags", wantMethod: "POST", wantPath: "/prompttags", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PostPromptTags(prompts.NewPostPromptTagsParams(), promptTestAuth) return err }, }, { name: "update prompt tag", wantID: "putPromptTags", wantMethod: "PUT", wantPath: "/prompttags", call: func(transport openapiruntime.ContextualTransport) error { _, err := prompts.New(transport, strfmt.Default).PutPromptTags(prompts.NewPutPromptTagsParams(), promptTestAuth) return err }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { transport := &promptCaptureTransport{} 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) } if transport.operation.AuthInfo == nil { t.Error("generated operation discarded its auth-info writer") } }) } } func TestPromptSpecRequiresExactCompoundAuthentication(t *testing.T) { specText := readMembersPromptSpec(t) for _, definition := range []string{ " ApiKeyAuth:\n type: \"apiKey\"\n in: \"header\"\n name: \"X-API-Key\"", " kvSessionCookie:\n name: Cookie\n in: header\n type: apiKey", } { if !strings.Contains(specText, definition) { t.Fatalf("authoritative spec is missing exact authentication definition:\n%s", definition) } } operations := map[string]map[string]string{ "/prompts": { "GET": "getPrompts", "POST": "postPrompts", "PUT": "putPrompts", }, "/promptanswers": { "GET": "getPromptAnswers", "POST": "postPromptAnswers", }, "/promptcategories": { "GET": "getPromptCategories", "POST": "postPromptCategories", "PUT": "putPromptCategories", }, "/prompttags": { "GET": "getPromptTags", "POST": "postPromptTags", "PUT": "putPromptTags", }, } for path, methods := range operations { for method, operationID := range methods { operation := promptSpecOperationBlock(t, specText, path, strings.ToLower(method)) if !strings.Contains(operation, " operationId: "+operationID+"\n") { t.Errorf("%s %s does not preserve operationId %q", method, path, operationID) } const compoundSecurity = " security:\n - ApiKeyAuth: []\n kvSessionCookie: []" if strings.Count(operation, compoundSecurity) != 1 { t.Errorf("%s %s does not require the exact API-key + human-session compound security", method, path) } } } } func TestPromptModelHasExactFullFieldParity(t *testing.T) { want := map[string]string{ "CreatedByID": "*string", "CreatedDate": "*string", "ID": "string", "Icon": "*string", "ImageAltText": "*string", "ImageURL": "*string", "LastModifiedByID": "*string", "LastModifiedDate": "*string", "Logo": "*string", "MaxTokens": "*float64", "Model": "*string", "Name": "*string", "Order": "*float64", "Parameters": "*string", "Prompt": "*string", "PromptCategoryID": "*string", "ResearchProjectIDs": "[]string", "Slug": "*string", "System": "*string", "Tags": "[]string", "Temperature": "*float64", "TenantID": "*string", "Title": "*string", "UsedCount": "*float64", "UserID": "*string", } model := reflect.TypeOf(members_models.Prompt{}) if model.NumField() != len(want) { t.Fatalf("Prompt has %d fields, want exact authoritative set of %d", model.NumField(), len(want)) } for name, wantType := range want { field, exists := model.FieldByName(name) if !exists { t.Errorf("Prompt is missing authoritative field %s", name) continue } if field.Type.String() != wantType { t.Errorf("Prompt.%s type = %s, want %s", name, field.Type, wantType) } if got := strings.Split(field.Tag.Get("json"), ",")[0]; got != name { t.Errorf("Prompt.%s JSON name = %q, want %q", name, got, name) } } maxTokens := 4096.0 body, err := json.Marshal(members_models.Prompt{MaxTokens: &maxTokens}) if err != nil { t.Fatalf("marshal Prompt: %v", err) } if !bytes.Contains(body, []byte(`"MaxTokens":4096`)) { t.Fatalf("Prompt JSON lost MaxTokens: %s", body) } } func TestPromptAnswerIsAppendOnlyAndLegacyMisspellingIsAbsent(t *testing.T) { contract := reflect.TypeOf((*prompts.ClientService)(nil)).Elem() for _, method := range []string{ "PutPromptAnswer", "PutPromptAnswerContext", "PutPromptAnswers", "PutPromptAnswersContext", "PutPromptAnsweer", "PutPromptAnsweerContext", "PutPromptAnsweers", "PutPromptAnsweersContext", } { if _, exists := contract.MethodByName(method); exists { t.Errorf("append-only PromptAnswer client exposes forbidden update %s", method) } } answerPath := promptSpecPathBlock(t, readMembersPromptSpec(t), "/promptanswers") for _, forbidden := range []string{" put:", " patch:", " delete:"} { if strings.Contains(answerPath, forbidden) { t.Fatalf("authoritative PromptAnswer contract exposes forbidden operation %q", strings.TrimSpace(forbidden)) } } } func TestPromptUpdateClientsPreserveConflictResponses(t *testing.T) { conflicts := []interface{ IsCode(int) bool }{ prompts.NewPutPromptsConflict(), prompts.NewPutPromptCategoriesConflict(), prompts.NewPutPromptTagsConflict(), } for _, conflict := range conflicts { if !conflict.IsCode(409) { t.Errorf("%T does not preserve the optimistic-conflict response", conflict) } } } func TestMembersSpecIsPinnedToPromptProviderSource(t *testing.T) { repoRoot := promptRepoRoot(t) mainSpec, err := os.ReadFile(filepath.Join(repoRoot, "swagger", "members-vernonkeenan.yaml")) if err != nil { t.Fatalf("read Members spec: %v", err) } sum := sha256.Sum256(mainSpec) if got := hex.EncodeToString(sum[:]); got != membersPromptProviderSpecSHA256 { t.Fatalf("Members spec drifted from provider commit 4e868f1: SHA-256 = %s, want %s", got, membersPromptProviderSpecSHA256) } externalSpec, err := os.ReadFile(filepath.Join(repoRoot, "swagger", "external", "members-vernonkeenan.yaml")) if err != nil { t.Fatalf("read external Members spec: %v", err) } normalized := bytes.ReplaceAll(externalSpec, []byte(`"https"`), []byte(`"http"`)) normalized = bytes.ReplaceAll(normalized, []byte("gw.tnxs.net"), []byte("members.vernonkeenan.com:8080")) normalized = bytes.ReplaceAll(normalized, []byte(`"/vk/members/v1"`), []byte(`"/v1"`)) if !bytes.Equal(normalized, mainSpec) { t.Fatal("external Members spec differs from the conventional host/scheme/base-path rewrite") } } func TestGeneratedPromptSurfaceHasNoSalesforceOrEmbeddedSecrets(t *testing.T) { repoRoot := promptRepoRoot(t) targets := []string{ filepath.Join(repoRoot, "api", "members", "members_client", "prompts"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_response.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_answer.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_answer_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_answer_response.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_category.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_category_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_category_response.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_tag.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_tag_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "prompt_tag_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 prompt target %s: %v", target, err) } } } func readMembersPromptSpec(t *testing.T) string { t.Helper() content, err := os.ReadFile(filepath.Join(promptRepoRoot(t), "swagger", "members-vernonkeenan.yaml")) if err != nil { t.Fatalf("read Members Swagger: %v", err) } return string(content) } func promptSpecPathBlock(t *testing.T, specText, path string) string { t.Helper() startMarker := " " + path + ":\n" start := strings.Index(specText, startMarker) if start < 0 { t.Fatalf("authoritative spec is missing path %s", path) } block := specText[start+len(startMarker):] if end := strings.Index(block, "\n /"); end >= 0 { block = block[:end] } return block } func promptSpecOperationBlock(t *testing.T, specText, path, method string) string { t.Helper() pathBlock := promptSpecPathBlock(t, specText, path) startMarker := " " + method + ":\n" start := strings.Index(pathBlock, startMarker) if start < 0 { t.Fatalf("authoritative spec is missing %s %s", strings.ToUpper(method), path) } block := pathBlock[start+len(startMarker):] methodBoundary := regexp.MustCompile(`(?m)^ [a-z]+:\n`) if end := methodBoundary.FindStringIndex(block); end != nil { block = block[:end[0]] } return block } func promptRepoRoot(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), "..", "..", "..")) }