package members_client_test import ( "context" "fmt" "os" "path/filepath" "reflect" "regexp" "strings" "testing" "code.tnxs.net/vernonkeenan/lib/api/members/members_client" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/clusters" "code.tnxs.net/vernonkeenan/lib/api/members/members_models" openapiruntime "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) var clusterTestAuth = openapiruntime.ClientAuthInfoWriterFunc( func(openapiruntime.ClientRequest, strfmt.Registry) error { return nil }, ) type clusterCaptureTransport struct { operation *openapiruntime.ClientOperation } func (transport *clusterCaptureTransport) Submit( operation *openapiruntime.ClientOperation, ) (any, error) { return transport.SubmitContext(context.Background(), operation) } func (transport *clusterCaptureTransport) SubmitContext( _ context.Context, operation *openapiruntime.ClientOperation, ) (any, error) { transport.operation = operation switch operation.ID { case "getClusters": return clusters.NewGetClustersOK(), nil case "postClusters": return clusters.NewPostClustersOK(), nil case "putClusters": return clusters.NewPutClustersOK(), nil default: panic("unexpected generated Cluster operation: " + operation.ID) } } func TestGeneratedClusterOperationContract(t *testing.T) { type operationCall func(openapiruntime.ContextualTransport) error tests := []struct { name, wantID, wantMethod, wantPath string call operationCall }{ { name: "list or get Cluster", wantID: "getClusters", wantMethod: "GET", wantPath: "/clusters", call: func(transport openapiruntime.ContextualTransport) error { _, err := clusters.New(transport, strfmt.Default).GetClusters( clusters.NewGetClustersParams().WithTenantID("tenant-example"), clusterTestAuth, ) return err }, }, { name: "create Cluster", wantID: "postClusters", wantMethod: "POST", wantPath: "/clusters", call: func(transport openapiruntime.ContextualTransport) error { _, err := clusters.New(transport, strfmt.Default).PostClusters( clusters.NewPostClustersParams().WithTenantID("tenant-example"), clusterTestAuth, ) return err }, }, { name: "CAS update Cluster", wantID: "putClusters", wantMethod: "PUT", wantPath: "/clusters", call: func(transport openapiruntime.ContextualTransport) error { _, err := clusters.New(transport, strfmt.Default).PutClusters( clusters.NewPutClustersParams().WithTenantID("tenant-example"), clusterTestAuth, ) return err }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { transport := &clusterCaptureTransport{} 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 compound auth-info writer") } }) } } func TestClusterTenantBindingCompoundAuthExactScopesAndCAS(t *testing.T) { specText := readMembersLearningSpec(t) pathBlock := learningSpecPathBlock(t, specText, "/clusters") for _, forbidden := range []string{" delete:", " patch:"} { if strings.Contains(pathBlock, forbidden) { t.Errorf("/clusters exposes unsupported %s", strings.TrimSpace(forbidden)) } } operations := map[string]struct { id, scope string }{ "get": {id: "getClusters", scope: "members:cluster:read"}, "post": {id: "postClusters", scope: "members:cluster:create"}, "put": {id: "putClusters", scope: "members:cluster:update"}, } const compoundSecurity = " security:\n - ApiKeyAuth: []\n kvSessionCookie: []" for method, expected := range operations { block := learningSpecOperationBlock(t, specText, "/clusters", method) if !strings.Contains(block, " operationId: "+expected.id+"\n") { t.Errorf("%s /clusters lost operationId %q", strings.ToUpper(method), expected.id) } if strings.Count(block, compoundSecurity) != 1 { t.Errorf("%s /clusters does not preserve compound authentication", strings.ToUpper(method)) } if !strings.Contains(block, expected.scope) { t.Errorf("%s /clusters lost exact scope %q", strings.ToUpper(method), expected.scope) } if !strings.Contains(block, `- $ref: "#/parameters/clusterTenantId"`) { t.Errorf("%s /clusters is not tenantId-bound", strings.ToUpper(method)) } } recordID := "cluster-id" getParams := clusters.NewGetClustersParams(). WithTenantID("tenant-example"). WithClusterID(&recordID) if getParams.TenantID != "tenant-example" { t.Fatal("Cluster list/get client lost the required tenantId") } if getParams.ClusterID == nil || *getParams.ClusterID != recordID { t.Fatal("Cluster list/get client lost its clusterId filter") } if clusters.NewPostClustersParams().WithTenantID("tenant-example").TenantID != "tenant-example" { t.Fatal("Cluster create client lost the required tenantId") } if clusters.NewPutClustersParams().WithTenantID("tenant-example").TenantID != "tenant-example" { t.Fatal("Cluster update client lost the required tenantId") } if !clusters.NewPutClustersConflict().IsCode(409) { t.Fatal("Cluster update client lost the optimistic concurrency response") } } func TestClusterModelAndSingleRecordRequestAreBounded(t *testing.T) { assertExactModelFields(t, members_models.Cluster{}, map[string]string{ "CreatedByID": "*string", "CreatedDate": "*string", "Description": "*string", "Environment": "*string", "Gateway": "*string", "ID": "string", "IPAddress": "*string", "LastModifiedByID": "*string", "LastModifiedDate": "*string", "Name": "*string", "OwnerID": "*string", "Ref": "*string", "Status": "*string", "Subnet": "*string", "TenantID": "*string", "Type": "*string", "Zone": "*string", }) firstName, secondName := "One", "Two" request := &members_models.ClusterRequest{ Data: []*members_models.Cluster{{Name: &firstName}, {Name: &secondName}}, } if err := request.Validate(strfmt.Default); err == nil { t.Fatal("generated Cluster request accepted a bulk payload") } if err := (&members_models.ClusterRequest{ Data: []*members_models.Cluster{{Name: &firstName}}, }).Validate(strfmt.Default); err != nil { t.Fatalf("generated Cluster request rejected one valid record: %v", err) } } func TestMembersCompoundAuthWritesBothCredentialsWithoutExposure(t *testing.T) { const ( apiKey = "test-api-key" sessionToken = "test-session-token" ) writer, err := members_client.NewMembersCompoundAuth(apiKey, sessionToken) if err != nil { t.Fatalf("construct compound auth writer: %v", err) } request := new(openapiruntime.TestClientRequest) if err := writer.AuthenticateRequest(request, strfmt.Default); err != nil { t.Fatalf("write compound authentication: %v", err) } if got := request.Headers.Get("X-API-Key"); got != apiKey { t.Errorf("X-API-Key = %q, want test value", got) } if got := request.Headers.Get("Cookie"); got != "kvSession="+sessionToken { t.Errorf("Cookie = %q, want kvSession test value", got) } formatted := fmt.Sprintf("%v", writer) if strings.Contains(formatted, apiKey) || strings.Contains(formatted, sessionToken) { t.Fatal("compound auth writer formatting exposed credential material") } } func TestMembersCompoundAuthRejectsMissingOrUnsafeCredentialsWithoutEcho(t *testing.T) { tests := []struct { name, apiKey, sessionToken string }{ {name: "missing API key", sessionToken: "session"}, {name: "missing session", apiKey: "api-key"}, {name: "API key header injection", apiKey: "api-key\r\nunsafe", sessionToken: "session"}, {name: "session cookie injection", apiKey: "api-key", sessionToken: "session;unsafe"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if writer, err := members_client.NewMembersCompoundAuth( test.apiKey, test.sessionToken, ); err == nil || writer != nil { t.Fatal("unsafe compound authentication was accepted") } else if (test.apiKey != "" && strings.Contains(err.Error(), test.apiKey)) || (test.sessionToken != "" && strings.Contains(err.Error(), test.sessionToken)) { t.Fatal("validation error exposed credential material") } }) } } func TestClusterSurfaceOmitsDeleteDatabaseAndDSN(t *testing.T) { contract := reflect.TypeOf((*clusters.ClientService)(nil)).Elem() for index := 0; index < contract.NumMethod(); index++ { method := contract.Method(index).Name for _, forbidden := range []string{"Delete", "Database", "DSN"} { if strings.Contains(method, forbidden) { t.Errorf("Cluster client exposes forbidden method %s", method) } } } root := reflect.TypeOf(members_client.Members{}) if _, exists := root.FieldByName("Clusters"); !exists { t.Fatal("root Members client does not expose governed Clusters") } } func TestGeneratedClusterSurfaceHasNoExternalBypassOrSecrets(t *testing.T) { repoRoot := learningRepoRoot(t) targets := []string{ filepath.Join(repoRoot, "api", "members", "members_client", "clusters"), filepath.Join(repoRoot, "api", "members", "members_client", "compound_auth.go"), filepath.Join(repoRoot, "api", "members", "members_models", "cluster.go"), filepath.Join(repoRoot, "api", "members", "members_models", "cluster_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "cluster_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-----", "members_client/databases", "dsn", } { 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 Cluster target %s: %v", target, err) } } }