Refactor: migrate POSIX scaffolding to CBOR and JSONC

This commit is contained in:
MangoPig
2026-07-06 13:06:32 +01:00
parent 2c8338d1cd
commit 58bc285748
14 changed files with 860 additions and 268 deletions
+91 -35
View File
@@ -7,6 +7,9 @@ import (
"path/filepath"
"strings"
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/tailscale/hujson"
)
type fakeRow struct {
@@ -104,25 +107,28 @@ func TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot(t *testing.T) {
}
requiredPaths := []string{
filepath.Join(rootPath, "settings.json"),
filepath.Join(rootPath, "layout.json"),
filepath.Join(rootPath, posixSettingsFileName),
filepath.Join(rootPath, posixLayoutFileName),
filepath.Join(rootPath, "catalog", "packs"),
filepath.Join(rootPath, "catalog", "standalone"),
filepath.Join(rootPath, "departments", "department-primary-department", "settings.json"),
filepath.Join(rootPath, "departments", "department-primary-department", "users.json"),
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", "settings.json"),
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", "users.json"),
filepath.Join(rootPath, "projects", "project-primary-project", "settings.json"),
filepath.Join(rootPath, "projects", "project-primary-project", "home.json"),
filepath.Join(rootPath, "projects", "project-primary-project", "acl.json"),
filepath.Join(rootPath, "catalog", "packs", "pack-core", posixManifestFileName),
filepath.Join(rootPath, "catalog", "packs", "pack-core", "entries", "app-shell", posixManifestFileName),
filepath.Join(rootPath, "catalog", "standalone", "app-shell", posixManifestFileName),
filepath.Join(rootPath, "departments", "department-primary-department", posixSettingsFileName),
filepath.Join(rootPath, "departments", "department-primary-department", posixUsersFileName),
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", posixSettingsFileName),
filepath.Join(rootPath, "departments", "department-primary-department", "teams", "team-primary-team", posixUsersFileName),
filepath.Join(rootPath, "projects", "project-primary-project", posixSettingsFileName),
filepath.Join(rootPath, "projects", "project-primary-project", posixHomeFileName),
filepath.Join(rootPath, "projects", "project-primary-project", posixACLFileName),
filepath.Join(rootPath, "projects", "project-primary-project", "children"),
filepath.Join(rootPath, "projects", "project-primary-project", "tree"),
filepath.Join(rootPath, "users", "settings.json"),
filepath.Join(rootPath, "users", "data.json"),
filepath.Join(rootPath, "users", posixSettingsFileName),
filepath.Join(rootPath, "users", posixDataFileName),
filepath.Join(rootPath, "users", "personals"),
filepath.Join(rootPath, "users", "personals", "personal-ronald", "settings.json"),
filepath.Join(rootPath, "users", "personals", "personal-ronald", "layout.json"),
filepath.Join(rootPath, "users", "personals", "personal-ronald", "home.json"),
filepath.Join(rootPath, "users", "personals", "personal-ronald", posixSettingsFileName),
filepath.Join(rootPath, "users", "personals", "personal-ronald", posixLayoutFileName),
filepath.Join(rootPath, "users", "personals", "personal-ronald", posixHomeFileName),
filepath.Join(rootPath, "users", "personals", "personal-ronald", "tree"),
}
@@ -132,10 +138,10 @@ func TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot(t *testing.T) {
}
}
settingsPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "settings.json"))
settingsPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, posixSettingsFileName))
installationPayload, ok := settingsPayload["installation"].(map[string]any)
if !ok {
t.Fatalf("settings.json missing installation object: %#v", settingsPayload)
t.Fatalf("%s missing installation object: %#v", posixSettingsFileName, settingsPayload)
}
if installationPayload["name"] != "MangoPig" {
t.Fatalf("expected installation name MangoPig, got %#v", installationPayload["name"])
@@ -144,31 +150,53 @@ func TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot(t *testing.T) {
t.Fatalf("expected installation to be bootstrapped, got %#v", installationPayload["isBootstrapped"])
}
layoutPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "layout.json"))
layoutPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, posixLayoutFileName))
homePayload, ok := layoutPayload["home"].(map[string]any)
if !ok {
t.Fatalf("layout.json missing home object: %#v", layoutPayload)
t.Fatalf("%s missing home object: %#v", posixLayoutFileName, layoutPayload)
}
if homePayload["defaultProjectSlug"] != "primary-project" {
t.Fatalf("expected default project slug primary-project, got %#v", homePayload["defaultProjectSlug"])
}
projectSettings := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "projects", "project-primary-project", "settings.json"))
packManifest := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "catalog", "packs", "pack-core", posixManifestFileName))
if packManifest["type"] != "pack" {
t.Fatalf("expected pack manifest type pack, got %#v", packManifest["type"])
}
if packManifest["slug"] != "core" {
t.Fatalf("expected pack manifest slug core, got %#v", packManifest["slug"])
}
entryManifest := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "catalog", "packs", "pack-core", "entries", "app-shell", posixManifestFileName))
runtimePayload, ok := entryManifest["runtime"].(map[string]any)
if !ok {
t.Fatalf("%s missing runtime object: %#v", posixManifestFileName, entryManifest)
}
if runtimePayload["path"] != "/v1/app-shell" {
t.Fatalf("expected app-shell runtime path /v1/app-shell, got %#v", runtimePayload["path"])
}
standaloneManifest := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "catalog", "standalone", "app-shell", posixManifestFileName))
if standaloneManifest["source"] != "standalone" {
t.Fatalf("expected standalone manifest source standalone, got %#v", standaloneManifest["source"])
}
projectSettings := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "projects", "project-primary-project", posixSettingsFileName))
if projectSettings["type"] != "project" {
t.Fatalf("expected project settings type project, got %#v", projectSettings["type"])
}
projectACL := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "projects", "project-primary-project", "acl.json"))
projectACL := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "projects", "project-primary-project", posixACLFileName))
if projectACL["inherits"] != true {
t.Fatalf("expected project acl to inherit by default, got %#v", projectACL["inherits"])
}
usersSettings := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "settings.json"))
usersSettings := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "users", posixSettingsFileName))
if usersSettings["primaryAdminId"] != "admin-1" {
t.Fatalf("expected primary admin id admin-1, got %#v", usersSettings["primaryAdminId"])
}
personalSettings := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "personals", "personal-ronald", "settings.json"))
personalSettings := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "personals", "personal-ronald", posixSettingsFileName))
if personalSettings["type"] != "personal" {
t.Fatalf("expected personal settings type personal, got %#v", personalSettings["type"])
}
@@ -179,7 +207,7 @@ func TestEnsureBootstrapPOSIXSkeletonInitializesEmptyRoot(t *testing.T) {
t.Fatalf("expected personal slug ronald, got %#v", personalSettings["slug"])
}
personalHome := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "personals", "personal-ronald", "home.json"))
personalHome := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, "users", "personals", "personal-ronald", posixHomeFileName))
if personalHome["type"] != "personal-home" {
t.Fatalf("expected personal home type personal-home, got %#v", personalHome["type"])
}
@@ -217,8 +245,8 @@ func TestCreateProjectHierarchyFolderOnDiskCreatesExpectedFolderShape(t *testing
createdFolderPath := filepath.Join(rootPath, "projects", "project-primary-project", "children", "folder-design-system")
for _, path := range []string{
filepath.Join(createdFolderPath, "folder.json"),
filepath.Join(createdFolderPath, "acl.json"),
filepath.Join(createdFolderPath, posixFolderFileName),
filepath.Join(createdFolderPath, posixACLFileName),
filepath.Join(createdFolderPath, "children"),
} {
if _, err := os.Stat(path); err != nil {
@@ -226,7 +254,7 @@ func TestCreateProjectHierarchyFolderOnDiskCreatesExpectedFolderShape(t *testing
}
}
folderPayload := readJSONFileForTest[map[string]any](t, filepath.Join(createdFolderPath, "folder.json"))
folderPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(createdFolderPath, posixFolderFileName))
if strings.TrimSpace(asStringForTest(folderPayload["id"])) == "" {
t.Fatalf("expected created folder to have stable id, got %#v", folderPayload["id"])
}
@@ -278,8 +306,8 @@ func TestCreateProjectTreeFolderOnDiskCreatesExpectedFolderShape(t *testing.T) {
createdFolderPath := filepath.Join(rootPath, "projects", "project-primary-project", "tree", "folder-docs")
for _, path := range []string{
filepath.Join(createdFolderPath, "folder.json"),
filepath.Join(createdFolderPath, "acl.json"),
filepath.Join(createdFolderPath, posixFolderFileName),
filepath.Join(createdFolderPath, posixACLFileName),
filepath.Join(createdFolderPath, "children"),
} {
if _, err := os.Stat(path); err != nil {
@@ -345,7 +373,7 @@ func TestRenameProjectHierarchyFolderOnDiskRenamesFolderShape(t *testing.T) {
t.Fatalf("expected nested child folder to move with renamed parent: %v", err)
}
folderPayload := readJSONFileForTest[map[string]any](t, filepath.Join(renamedFolderPath, "folder.json"))
folderPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(renamedFolderPath, posixFolderFileName))
if strings.TrimSpace(asStringForTest(folderPayload["id"])) == "" {
t.Fatalf("expected renamed folder to preserve stable id, got %#v", folderPayload["id"])
}
@@ -392,7 +420,7 @@ func TestRenameProjectTreeFolderOnDiskRenamesFolderShape(t *testing.T) {
t.Fatalf("unexpected renamed path: %s", renamedPath)
}
folderPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, filepath.FromSlash(renamedPath), "folder.json"))
folderPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, filepath.FromSlash(renamedPath), posixFolderFileName))
if folderPayload["name"] != "Specifications" {
t.Fatalf("expected renamed folder name Specifications, got %#v", folderPayload["name"])
}
@@ -454,7 +482,7 @@ func TestMoveProjectHierarchyFolderOnDiskMovesFolderToNewParent(t *testing.T) {
t.Fatalf("expected nested child folder to move with moved parent: %v", err)
}
folderPayload := readJSONFileForTest[map[string]any](t, filepath.Join(movedFolderPath, "folder.json"))
folderPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(movedFolderPath, posixFolderFileName))
if strings.TrimSpace(asStringForTest(folderPayload["id"])) == "" {
t.Fatalf("expected moved folder to preserve stable id, got %#v", folderPayload["id"])
}
@@ -505,7 +533,7 @@ func TestMoveProjectTreeFolderOnDiskMovesFolderToNewParent(t *testing.T) {
t.Fatalf("unexpected moved path: %s", movedPath)
}
folderPayload := readJSONFileForTest[map[string]any](t, filepath.Join(rootPath, filepath.FromSlash(movedPath), "folder.json"))
folderPayload := readStructuredFileForTest[map[string]any](t, filepath.Join(rootPath, filepath.FromSlash(movedPath), posixFolderFileName))
if folderPayload["name"] != "Docs" {
t.Fatalf("expected moved folder name Docs, got %#v", folderPayload["name"])
}
@@ -622,7 +650,7 @@ func asStringForTest(value any) string {
return text
}
func readJSONFileForTest[T any](t *testing.T, path string) T {
func readStructuredFileForTest[T any](t *testing.T, path string) T {
t.Helper()
data, err := os.ReadFile(path)
@@ -630,9 +658,37 @@ func readJSONFileForTest[T any](t *testing.T, path string) T {
t.Fatalf("read %s: %v", path, err)
}
var raw any
switch strings.ToLower(filepath.Ext(path)) {
case ".cbor":
if err := cbor.Unmarshal(data, &raw); err != nil {
t.Fatalf("unmarshal %s: %v", path, err)
}
case ".json":
if err := json.Unmarshal(data, &raw); err != nil {
t.Fatalf("unmarshal %s: %v", path, err)
}
case ".jsonc":
ast, err := hujson.Parse(data)
if err != nil {
t.Fatalf("parse jsonc %s: %v", path, err)
}
ast.Standardize()
if err := json.Unmarshal(ast.Pack(), &raw); err != nil {
t.Fatalf("unmarshal standardized %s: %v", path, err)
}
default:
t.Fatalf("unsupported structured test file %s", path)
}
normalizedBytes, err := json.Marshal(normalizeStructuredValue(raw))
if err != nil {
t.Fatalf("normalize %s: %v", path, err)
}
var payload T
if err := json.Unmarshal(data, &payload); err != nil {
t.Fatalf("unmarshal %s: %v", path, err)
if err := json.Unmarshal(normalizedBytes, &payload); err != nil {
t.Fatalf("decode normalized %s: %v", path, err)
}
return payload