package workspaceops import ( "encoding/base64" "os" "path/filepath" "reflect" "strings" "testing" "context" "github.com/aritmolab/thothii/tools/thothctl/internal/compose" "github.com/aritmolab/thothii/tools/thothctl/internal/config" ) func TestParseWorkspaceCommands(t *testing.T) { cases := []struct { name string argv []string want any }{ {"inspect", []string{"workspace", "inspect", "--workspace", "psd", "--json"}, InspectCommand{WorkspaceID: "psd", JSON: true}}, {"dwh", []string{"workspace", "preprocess", "dwh", "--workspace", "psd", "--resume", "0123456789abcdef0123456789abcdef"}, DwhRequest{WorkspaceID: "psd", Resume: "0123456789abcdef0123456789abcdef"}}, {"suggest", []string{"workspace", "schema", "suggest-fks", "--workspace", "psd", "--assume", "a=b"}, SuggestFksRequest{WorkspaceID: "psd", Assume: []string{"a=b"}}}, {"check", []string{"workspace", "schema", "check", "--workspace", "psd", "--resume", "0123456789abcdef0123456789abcdef"}, CheckSchemaRequest{WorkspaceID: "psd", Resume: "0123456789abcdef0123456789abcdef"}}, {"index", []string{"workspace", "index-schema", "--workspace", "psd"}, IndexSchemaRequest{WorkspaceID: "psd"}}, {"evidence", []string{"workspace", "preprocess", "evidence", "--workspace", "psd", "--dry-run"}, EvidenceRequest{WorkspaceID: "psd", DryRun: true}}, {"run", []string{"workspace", "preprocess", "run", "--workspace", "psd"}, RunRequest{WorkspaceID: "psd"}}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, err := ParseWorkspaceCommand(tc.argv) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, tc.want) { t.Errorf("got %#v want %#v", got, tc.want) } }) } } func TestParseWorkspaceRejectsUnsafeOrAmbiguousOptions(t *testing.T) { bad := [][]string{ {"workspace", "inspect", "--workspace", "psd", "--resume", "0123456789abcdef0123456789abcdef"}, {"workspace", "inspect", "--workspace", "psd", "--bootstrap-run-id", "0123456789abcdef0123456789abcdef"}, {"workspace", "inspect", "--workspace", "psd", "--passthrough"}, {"workspace", "preprocess", "run", "--workspace", "PSd"}, {"workspace", "preprocess", "run", "--workspace", "psd", "--resume", "bad"}, {"workspace", "schema", "check", "--workspace", "psd"}, {"workspace", "schema", "check", "--workspace", "psd", "--resume", "0123456789abcdef0123456789abcdef", "--annotations", "a"}, } for _, argv := range bad { if _, err := ParseWorkspaceCommand(argv); err == nil { t.Errorf("accepted unsafe argv %v", argv) } } } func TestParseWorkspaceRejectsNonContractHierarchyAndAssumeShell(t *testing.T) { for _, argv := range [][]string{ {"workspace", "schema", "dwh", "--workspace", "psd"}, {"workspace", "preprocess", "check", "--workspace", "psd"}, {"workspace", "suggest-fks", "--workspace", "psd"}, {"workspace", "inspect", "--workspace", "psd", "--bootstrap-run-id", strings.Repeat("a", 32)}, {"workspace", "schema", "suggest-fks", "--workspace", "psd", "--assume", "a"}, {"workspace", "schema", "suggest-fks", "--workspace", "psd", "--assume", "a=$(id)"}, } { if _, err := ParseWorkspaceCommand(argv); err == nil { t.Errorf("accepted non-contract argv %v", argv) } } } func TestRunUsesBase64BasenameIngressAndNoTTY(t *testing.T) { root, err := filepath.EvalSymlinks(t.TempDir()) if err != nil { t.Fatal(err) } sqlPath := filepath.Join(root, "schema.sql") if err := os.WriteFile(sqlPath, []byte("select 1"), 0o600); err != nil { t.Fatal(err) } out := filepath.Join(root, "candidate.yaml") resultJSON := `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"psd","workspaceRevision":"0123456789012345678901234567890123456789","descriptorBlob":"abcdefabcdefabcdefabcdefabcdefabcdefabcd","operation":"suggest-fks","runId":"0123456789abcdef0123456789abcdef","completedStages":[]}` script := "#!/bin/sh\ncat >/dev/null\nprintf '%s' '" + resultJSON + "'\n" fake := filepath.Join(root, "docker") if err := os.WriteFile(fake, []byte(script), 0o700); err != nil { t.Fatal(err) } cmd, err := ParseWorkspaceCommand([]string{"workspace", "schema", "suggest-fks", "--workspace", "psd", "--from-sql", sqlPath}) if err != nil { t.Fatal(err) } got, err := Run(context.Background(), config.Installation{ProjectDirectory: root, EnvFile: filepath.Join(root, "env")}, compose.NewRunner(fake), cmd, nil) if err != nil { t.Fatal(err) } if got.Code != "ok" { t.Fatalf("result = %#v", got) } _ = out } func TestRunPublishesOnlyVerifiedCandidateExport(t *testing.T) { root, err := filepath.EvalSymlinks(t.TempDir()) if err != nil { t.Fatal(err) } candidate := []byte("candidates: []\n") digest := DigestBytes(candidate) encoded := base64.StdEncoding.EncodeToString(candidate) resultJSON := `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"psd","workspaceRevision":"0123456789012345678901234567890123456789","descriptorBlob":"abcdefabcdefabcdefabcdefabcdefabcdefabcd","operation":"suggest-fks","runId":"0123456789abcdef0123456789abcdef","completedStages":[],"artifactIdentities":[{"kind":"fk-candidates","digest":"` + digest + `"}],"hostExport":{"mediaType":"application/yaml","sha256":"` + digest + `","contentBase64":"` + encoded + `"}}` fake := filepath.Join(root, "docker") if err := os.WriteFile(fake, []byte("#!/bin/sh\ncat >/dev/null\nprintf '%s' '"+resultJSON+"'\n"), 0o700); err != nil { t.Fatal(err) } path := filepath.Join(root, "out.yaml") cmd, err := ParseWorkspaceCommand([]string{"workspace", "schema", "suggest-fks", "--workspace", "psd", "--output", path}) if err != nil { t.Fatal(err) } got, err := Run(context.Background(), config.Installation{ProjectDirectory: root, EnvFile: filepath.Join(root, "env")}, compose.NewRunner(fake), cmd, nil) if err != nil { t.Fatal(err) } if got.RunID == "" { t.Fatal("missing run identity") } b, err := os.ReadFile(path) if err != nil || string(b) != string(candidate) { t.Fatalf("candidate = %q, %v", b, err) } }