package handlers import ( "archive/zip" "context" "encoding/json" "os" "path/filepath" "testing" "armature/database" "armature/store" postgres "armature/store/postgres" sqlite "armature/store/sqlite" ) // ── Test helpers ────────────────────────────── func newTestStores(t *testing.T) store.Stores { t.Helper() database.RequireTestDB(t) database.TruncateAll(t) if database.IsSQLite() { return sqlite.NewStores(database.TestDB) } return postgres.NewStores(database.TestDB) } // buildTestPkg creates a minimal .pkg archive in dir with the given manifest. func buildTestPkg(t *testing.T, dir string, manifest map[string]any) string { t.Helper() pkgID, _ := manifest["id"].(string) if pkgID == "" { t.Fatal("manifest must have 'id'") } data, err := json.Marshal(manifest) if err != nil { t.Fatal(err) } pkgPath := filepath.Join(dir, pkgID+".pkg") f, err := os.Create(pkgPath) if err != nil { t.Fatal(err) } defer f.Close() zw := zip.NewWriter(f) w, err := zw.Create("manifest.json") if err != nil { t.Fatal(err) } if _, err := w.Write(data); err != nil { t.Fatal(err) } if err := zw.Close(); err != nil { t.Fatal(err) } return pkgPath } // ═══════════════════════════════════════════════ // Tests // ═══════════════════════════════════════════════ // TestBundledInstall_FreshDB verifies that bundled packages are installed // into an empty database on first run. func TestBundledInstall_FreshDB(t *testing.T) { stores := newTestStores(t) ctx := context.Background() bundledDir := t.TempDir() packagesDir := t.TempDir() buildTestPkg(t, bundledDir, map[string]any{ "id": "test-surface-a", "title": "Test Surface A", "type": "surface", }) buildTestPkg(t, bundledDir, map[string]any{ "id": "test-surface-b", "title": "Test Surface B", "type": "surface", "route": "/s/test-b", }) InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil) // Both packages should be installed and enabled for _, id := range []string{"test-surface-a", "test-surface-b"} { pkg, err := stores.Packages.Get(ctx, id) if err != nil || pkg == nil { t.Fatalf("package %s not found after install", id) } if !pkg.Enabled { t.Errorf("package %s should be enabled", id) } if pkg.Source != "bundled" { t.Errorf("package %s source = %q, want %q", id, pkg.Source, "bundled") } } } // TestBundledInstall_SkipsExisting verifies that already-installed packages // are not re-installed (preserves admin uninstall/reconfigure). func TestBundledInstall_SkipsExisting(t *testing.T) { stores := newTestStores(t) ctx := context.Background() bundledDir := t.TempDir() packagesDir := t.TempDir() // Pre-seed a package with same ID but different title (simulating existing install) manifest := map[string]any{"id": "pre-existing", "title": "Original Title", "type": "surface"} if err := stores.Packages.Seed(ctx, "pre-existing", "Original Title", "extension", manifest); err != nil { t.Fatal(err) } // Bundled package has same ID but different title buildTestPkg(t, bundledDir, map[string]any{ "id": "pre-existing", "title": "Bundled Title", "type": "surface", }) InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil) // Package should retain original title (not overwritten) pkg, err := stores.Packages.Get(ctx, "pre-existing") if err != nil || pkg == nil { t.Fatal("pre-existing package not found") } if pkg.Title != "Original Title" { t.Errorf("title = %q, want %q (should not overwrite existing)", pkg.Title, "Original Title") } } // TestBundledInstall_MissingDir verifies graceful handling when bundled // packages directory doesn't exist. func TestBundledInstall_MissingDir(t *testing.T) { stores := newTestStores(t) // Should not panic or error — just log and return InstallBundledPackages("/nonexistent/path", "", "", stores, nil) } // TestBundledInstall_EmptyDir verifies no-op when directory exists but is empty. func TestBundledInstall_EmptyDir(t *testing.T) { stores := newTestStores(t) bundledDir := t.TempDir() // Should not panic or error InstallBundledPackages(bundledDir, "", "", stores, nil) } // TestBundledInstall_DormantPackage verifies that bundled packages with // unmet requires are marked dormant. func TestBundledInstall_DormantPackage(t *testing.T) { stores := newTestStores(t) ctx := context.Background() bundledDir := t.TempDir() packagesDir := t.TempDir() buildTestPkg(t, bundledDir, map[string]any{ "id": "dormant-pkg", "title": "Dormant Package", "type": "extension", "hooks": []string{"on_install"}, "requires": []string{"chat"}, }) InstallBundledPackages(bundledDir, packagesDir, "*", stores, nil) pkg, err := stores.Packages.Get(ctx, "dormant-pkg") if err != nil || pkg == nil { t.Fatal("dormant package not found after install") } if pkg.Status != "dormant" { t.Errorf("status = %q, want %q", pkg.Status, "dormant") } if pkg.Enabled { t.Error("dormant package should not be enabled") } } // TestBundledInstall_Allowlist verifies that BUNDLED_PACKAGES allowlist // filters which packages are installed. func TestBundledInstall_Allowlist(t *testing.T) { stores := newTestStores(t) ctx := context.Background() bundledDir := t.TempDir() packagesDir := t.TempDir() buildTestPkg(t, bundledDir, map[string]any{ "id": "pkg-alpha", "title": "Alpha", "type": "surface", }) buildTestPkg(t, bundledDir, map[string]any{ "id": "pkg-beta", "title": "Beta", "type": "surface", }) buildTestPkg(t, bundledDir, map[string]any{ "id": "pkg-gamma", "title": "Gamma", "type": "surface", }) // Only allow alpha and gamma InstallBundledPackages(bundledDir, packagesDir, "pkg-alpha,pkg-gamma", stores, nil) // Alpha and gamma should be installed for _, id := range []string{"pkg-alpha", "pkg-gamma"} { pkg, err := stores.Packages.Get(ctx, id) if err != nil || pkg == nil { t.Fatalf("package %s should have been installed (in allowlist)", id) } } // Beta should NOT be installed pkg, _ := stores.Packages.Get(ctx, "pkg-beta") if pkg != nil { t.Fatal("pkg-beta should NOT have been installed (not in allowlist)") } }