Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

492 lines
17 KiB
Go

package workspace
import (
"context"
"errors"
"testing"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
)
func TestSQLiteStoreWorkspaceAppsPersistPackagesAndInstallations(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-apps", Name: "Workspace Apps"}); err != nil {
t.Fatalf("Create() error = %v", err)
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: "hello",
Version: "0.1.0",
PackageDir: "/tmp/hello",
Manifest: workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "hello",
Version: "0.1.0",
Name: "Hello",
Description: "Minimal app",
Icon: workspacebiz.AppManifestIcon{
Type: "asset",
Src: "icon.png",
},
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
},
}); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
packages, err := store.ListAppPackages(ctx)
if err != nil {
t.Fatalf("ListAppPackages() error = %v", err)
}
if len(packages) != 1 || packages[0].AppID != "hello" || packages[0].PackageDir != "/tmp/hello" {
t.Fatalf("ListAppPackages() = %#v", packages)
}
if packages[0].DisplayName() != "Hello" || packages[0].Description() != "Minimal app" || packages[0].Manifest.Runtime.HealthcheckPath != "/ready" || packages[0].ManifestJSON == "" {
t.Fatalf("ListAppPackages() manifest = %#v", packages[0])
}
if err := store.PutWorkspaceAppInstallation(ctx, workspacebiz.AppInstallation{
WorkspaceID: "ws-apps",
AppID: "hello",
Enabled: true,
}); err != nil {
t.Fatalf("PutWorkspaceAppInstallation() error = %v", err)
}
installations, err := store.ListWorkspaceAppInstallations(ctx, "ws-apps")
if err != nil {
t.Fatalf("ListWorkspaceAppInstallations() error = %v", err)
}
if len(installations) != 1 || installations[0].AppID != "hello" || !installations[0].Enabled {
t.Fatalf("ListWorkspaceAppInstallations() = %#v", installations)
}
installationsByApp, err := store.ListWorkspaceAppInstallationsByApp(ctx, "hello")
if err != nil {
t.Fatalf("ListWorkspaceAppInstallationsByApp() error = %v", err)
}
if len(installationsByApp) != 1 || installationsByApp[0].WorkspaceID != "ws-apps" || !installationsByApp[0].Enabled {
t.Fatalf("ListWorkspaceAppInstallationsByApp() = %#v", installationsByApp)
}
if err := store.DeleteWorkspaceAppInstallation(ctx, "ws-apps", "hello"); err != nil {
t.Fatalf("DeleteWorkspaceAppInstallation() error = %v", err)
}
installations, err = store.ListWorkspaceAppInstallations(ctx, "ws-apps")
if err != nil {
t.Fatalf("ListWorkspaceAppInstallations() after delete error = %v", err)
}
if len(installations) != 0 {
t.Fatalf("installations after delete = %#v, want empty", installations)
}
if err := store.DeleteWorkspaceAppInstallation(ctx, "ws-apps", "hello"); !errors.Is(err, ErrWorkspaceAppNotFound) {
t.Fatalf("DeleteWorkspaceAppInstallation() missing error = %v", err)
}
}
func TestSQLiteStorePersistsLocalDevAppPackageSource(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
appPackage := workspacebiz.AppPackage{
AppID: "local-dev-app",
Version: "0.1.0",
PackageDir: "/Users/example/project/.tutti/dev-app",
Manifest: workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "local-dev-app",
Version: "0.1.0",
Name: "Local Dev App",
Description: "Local app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "bootstrap.sh",
HealthcheckPath: "/",
},
},
Source: workspacebiz.AppPackageSourceLocalDev,
}
if err := store.PutAppPackage(ctx, appPackage); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
active, err := store.GetAppPackage(ctx, appPackage.AppID)
if err != nil {
t.Fatalf("GetAppPackage() error = %v", err)
}
if active.Source != workspacebiz.AppPackageSourceLocalDev || active.PackageDir != appPackage.PackageDir {
t.Fatalf("active package = %#v", active)
}
versions, err := store.ListAppPackageVersions(ctx, appPackage.AppID)
if err != nil {
t.Fatalf("ListAppPackageVersions() error = %v", err)
}
if len(versions) != 1 || versions[0].Source != workspacebiz.AppPackageSourceLocalDev {
t.Fatalf("versions = %#v", versions)
}
}
func TestSQLiteStoreListAppPackagesSkipsInvalidActivePackage(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
validManifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "valid-app",
Version: "1.0.0",
Name: "Valid App",
Description: "Valid app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: validManifest.AppID,
Version: validManifest.Version,
PackageDir: "/tmp/valid-app",
Manifest: validManifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
const invalidManifestJSON = `{"schemaVersion":"tutti.app.manifest.v1","appId":"invalid-app","version":"1.0.0","name":"Invalid App","description":"Invalid app","runtime":{"bootstrap":"start.sh","healthcheckPath":"/ready"},"references":{"searchEndpoint":"/references/search"}}`
if _, err := store.writeDB.ExecContext(ctx, `
INSERT INTO app_packages (
app_id, version, package_dir, manifest_json, source, factory_job_id, created_in_workspace_id, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, '', '', 1, 1)
`, "invalid-app", "1.0.0", "/tmp/invalid-app", invalidManifestJSON, string(workspacebiz.AppPackageSourceBuiltin)); err != nil {
t.Fatalf("insert invalid app package error = %v", err)
}
if _, err := store.writeDB.ExecContext(ctx, `
INSERT INTO app_catalog_entries (
app_id, active_version, source, created_in_workspace_id, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, '', 1, 1)
`, "invalid-app", "1.0.0", string(workspacebiz.AppPackageSourceBuiltin)); err != nil {
t.Fatalf("insert invalid app catalog entry error = %v", err)
}
packages, err := store.ListAppPackages(ctx)
if err != nil {
t.Fatalf("ListAppPackages() error = %v", err)
}
if len(packages) != 1 || packages[0].AppID != "valid-app" {
t.Fatalf("ListAppPackages() = %#v, want only valid-app", packages)
}
}
func TestSQLiteStoreListAppPackageFileRecordsAllowsInvalidManifest(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
const invalidManifestJSON = `{"schemaVersion":"tutti.app.manifest.v1","appId":"invalid-app","version":"1.0.0","name":"Invalid App","description":"Invalid app","runtime":{"bootstrap":"start.sh","healthcheckPath":"/ready"},"references":{"searchEndpoint":"/references/search"}}`
if _, err := store.writeDB.ExecContext(ctx, `
INSERT INTO app_packages (
app_id, version, package_dir, manifest_json, source, factory_job_id, created_in_workspace_id, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, '', '', 1, 1)
`, "invalid-app", "1.0.0", "/tmp/invalid-app", invalidManifestJSON, string(workspacebiz.AppPackageSourceBuiltin)); err != nil {
t.Fatalf("insert invalid app package error = %v", err)
}
if _, err := store.ListAppPackageVersions(ctx, "invalid-app"); err == nil {
t.Fatal("ListAppPackageVersions() error = nil, want invalid manifest error")
}
records, err := store.ListAppPackageFileRecords(ctx, "invalid-app")
if err != nil {
t.Fatalf("ListAppPackageFileRecords() error = %v", err)
}
if len(records) != 1 {
t.Fatalf("records length = %d, want 1: %#v", len(records), records)
}
if records[0].AppID != "invalid-app" || records[0].Version != "1.0.0" || records[0].PackageDir != "/tmp/invalid-app" || records[0].Source != workspacebiz.AppPackageSourceBuiltin {
t.Fatalf("record = %#v", records[0])
}
}
func TestSQLiteStoreListAppPackagesRepairsInvalidActivePackageToValidVersion(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
validManifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "repair-app",
Version: "1.0.0",
Name: "Repair App",
Description: "Repair app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: validManifest.AppID,
Version: validManifest.Version,
PackageDir: "/tmp/repair-app-1.0.0",
Manifest: validManifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
const invalidManifestJSON = `{"schemaVersion":"tutti.app.manifest.v1","appId":"repair-app","version":"1.1.0","name":"Repair App","description":"Repair app","runtime":{"bootstrap":"start.sh","healthcheckPath":"/ready"},"references":{"searchEndpoint":"/references/search"}}`
if _, err := store.writeDB.ExecContext(ctx, `
INSERT INTO app_packages (
app_id, version, package_dir, manifest_json, source, factory_job_id, created_in_workspace_id, created_at_unix_ms, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, '', '', 2, 2)
`, "repair-app", "1.1.0", "/tmp/repair-app-1.1.0", invalidManifestJSON, string(workspacebiz.AppPackageSourceBuiltin)); err != nil {
t.Fatalf("insert invalid app package error = %v", err)
}
if _, err := store.writeDB.ExecContext(ctx, `
UPDATE app_catalog_entries
SET active_version = ?, updated_at_unix_ms = 2
WHERE app_id = ?
`, "1.1.0", "repair-app"); err != nil {
t.Fatalf("activate invalid app package error = %v", err)
}
packages, err := store.ListAppPackages(ctx)
if err != nil {
t.Fatalf("ListAppPackages() error = %v", err)
}
if len(packages) != 1 || packages[0].AppID != "repair-app" || packages[0].Version != "1.0.0" {
t.Fatalf("ListAppPackages() = %#v, want repaired repair-app 1.0.0", packages)
}
stored, err := store.GetAppPackage(ctx, "repair-app")
if err != nil {
t.Fatalf("GetAppPackage() after repair error = %v", err)
}
if stored.Version != "1.0.0" {
t.Fatalf("GetAppPackage() version = %q, want 1.0.0", stored.Version)
}
}
func TestSQLiteStoreDeleteAppPackageRemovesCatalogVersionsAndInstallations(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-apps", Name: "Workspace Apps"}); err != nil {
t.Fatalf("Create() error = %v", err)
}
manifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "local-app",
Version: "0.1.0",
Name: "Local App",
Description: "Local app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/local-app-0.1.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceGenerated,
}); err != nil {
t.Fatalf("PutAppPackage() first error = %v", err)
}
manifest.Version = "0.2.0"
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/local-app-0.2.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceGenerated,
}); err != nil {
t.Fatalf("PutAppPackage() second error = %v", err)
}
if err := store.PutWorkspaceAppInstallation(ctx, workspacebiz.AppInstallation{
WorkspaceID: "ws-apps",
AppID: "local-app",
Enabled: true,
}); err != nil {
t.Fatalf("PutWorkspaceAppInstallation() error = %v", err)
}
if err := store.DeleteAppPackage(ctx, "local-app"); err != nil {
t.Fatalf("DeleteAppPackage() error = %v", err)
}
if _, err := store.GetAppPackage(ctx, "local-app"); !errors.Is(err, ErrWorkspaceAppNotFound) {
t.Fatalf("GetAppPackage() after delete error = %v, want ErrWorkspaceAppNotFound", err)
}
versions, err := store.ListAppPackageVersions(ctx, "local-app")
if err != nil {
t.Fatalf("ListAppPackageVersions() after delete error = %v", err)
}
if len(versions) != 0 {
t.Fatalf("versions after delete = %#v, want empty", versions)
}
installations, err := store.ListWorkspaceAppInstallations(ctx, "ws-apps")
if err != nil {
t.Fatalf("ListWorkspaceAppInstallations() after delete error = %v", err)
}
if len(installations) != 0 {
t.Fatalf("installations after delete = %#v, want empty", installations)
}
}
func TestSQLiteStorePutAppPackageVersionDoesNotActivateVersion(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
manifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "remote-app",
Version: "1.0.0",
Name: "Remote App v1",
Description: "Remote app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/remote-app-1.0.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
manifest.Version = "1.1.0"
manifest.Name = "Remote App v2"
if err := store.PutAppPackageVersion(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/remote-app-1.1.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackageVersion() error = %v", err)
}
active, err := store.GetAppPackage(ctx, "remote-app")
if err != nil {
t.Fatalf("GetAppPackage() error = %v", err)
}
if active.Version != "1.0.0" {
t.Fatalf("active version = %q, want 1.0.0", active.Version)
}
versions, err := store.ListAppPackageVersions(ctx, "remote-app")
if err != nil {
t.Fatalf("ListAppPackageVersions() error = %v", err)
}
if len(versions) != 2 {
t.Fatalf("versions length = %d, want 2: %#v", len(versions), versions)
}
if err := store.SetActiveAppPackageVersion(ctx, "remote-app", "1.1.0"); err != nil {
t.Fatalf("SetActiveAppPackageVersion() error = %v", err)
}
active, err = store.GetAppPackage(ctx, "remote-app")
if err != nil {
t.Fatalf("GetAppPackage() after activate error = %v", err)
}
if active.Version != "1.1.0" {
t.Fatalf("active version after activate = %q, want 1.1.0", active.Version)
}
}
func TestSQLiteStoreDeleteAppPackageVersionRemovesOnlyInactiveVersion(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
manifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "remote-app",
Version: "1.0.0",
Name: "Remote App v1",
Description: "Remote app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackage(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/remote-app-1.0.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackage() error = %v", err)
}
manifest.Version = "1.1.0"
if err := store.PutAppPackageVersion(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/remote-app-1.1.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackageVersion() error = %v", err)
}
if err := store.DeleteAppPackageVersion(ctx, "remote-app", "1.0.0"); err == nil {
t.Fatal("DeleteAppPackageVersion(active) error = nil, want active delete error")
}
if err := store.DeleteAppPackageVersion(ctx, "remote-app", "1.1.0"); err != nil {
t.Fatalf("DeleteAppPackageVersion(inactive) error = %v", err)
}
if _, err := store.GetAppPackageVersion(ctx, "remote-app", "1.1.0"); !errors.Is(err, ErrWorkspaceAppNotFound) {
t.Fatalf("GetAppPackageVersion(deleted) error = %v, want ErrWorkspaceAppNotFound", err)
}
active, err := store.GetAppPackage(ctx, "remote-app")
if err != nil {
t.Fatalf("GetAppPackage(active) error = %v", err)
}
if active.Version != "1.0.0" {
t.Fatalf("active version = %q, want 1.0.0", active.Version)
}
}
func TestSQLiteStorePutAppPackageVersionCreatesInitialCatalogEntry(t *testing.T) {
t.Parallel()
store := openTestSQLiteStore(t)
ctx := context.Background()
manifest := workspacebiz.AppManifest{
SchemaVersion: workspacebiz.AppManifestSchemaVersionV1,
AppID: "remote-app",
Version: "1.0.0",
Name: "Remote App",
Description: "Remote app",
Runtime: workspacebiz.AppManifestRuntime{
Bootstrap: "start.sh",
HealthcheckPath: "/ready",
},
}
if err := store.PutAppPackageVersion(ctx, workspacebiz.AppPackage{
AppID: manifest.AppID,
Version: manifest.Version,
PackageDir: "/tmp/remote-app-1.0.0",
Manifest: manifest,
Source: workspacebiz.AppPackageSourceBuiltin,
}); err != nil {
t.Fatalf("PutAppPackageVersion() error = %v", err)
}
active, err := store.GetAppPackage(ctx, "remote-app")
if err != nil {
t.Fatalf("GetAppPackage() error = %v", err)
}
if active.Version != "1.0.0" {
t.Fatalf("active version = %q, want 1.0.0", active.Version)
}
}