diff --git a/datanode/client.go b/datanode/medium.go similarity index 99% rename from datanode/client.go rename to datanode/medium.go index 05e11e6..49979fa 100644 --- a/datanode/client.go +++ b/datanode/medium.go @@ -1,4 +1,4 @@ -// Package datanode keeps io.Medium data in Borg's DataNode. +// Package datanode provides an io.Medium implementation backed by Borg's DataNode. // // medium := datanode.New() // _ = medium.Write("jobs/run.log", "started") diff --git a/datanode/client_test.go b/datanode/medium_test.go similarity index 99% rename from datanode/client_test.go rename to datanode/medium_test.go index 123e8c8..89084eb 100644 --- a/datanode/client_test.go +++ b/datanode/medium_test.go @@ -96,7 +96,7 @@ func TestClient_Delete_Good(t *testing.T) { func TestClient_Delete_Bad(t *testing.T) { m := New() - // Delete non-existent + // Example: m.Delete("ghost.txt") assert.Error(t, m.Delete("ghost.txt")) // Delete non-empty dir diff --git a/local/client.go b/local/medium.go similarity index 99% rename from local/client.go rename to local/medium.go index 3eebd11..5f80f06 100644 --- a/local/client.go +++ b/local/medium.go @@ -1,4 +1,4 @@ -// Package local binds io.Medium to the local filesystem. +// Package local provides the io.Medium implementation for the local filesystem. // // medium, _ := local.New("/srv/app") // _ = medium.Write("config/app.yaml", "port: 8080") diff --git a/local/client_test.go b/local/medium_test.go similarity index 99% rename from local/client_test.go rename to local/medium_test.go index 50e70e3..8a22b8b 100644 --- a/local/client_test.go +++ b/local/medium_test.go @@ -15,7 +15,7 @@ func TestClient_New_ResolvesRoot_Good(t *testing.T) { root := t.TempDir() m, err := New(root) assert.NoError(t, err) - // New() resolves symlinks (macOS /var → /private/var), so compare resolved paths. + // Example: local.New("/srv/app") resolves macOS "/var" to "/private/var" before sandbox checks. resolved, err := resolveSymlinksPath(root) require.NoError(t, err) assert.Equal(t, resolved, m.filesystemRoot) diff --git a/client_test.go b/medium_test.go similarity index 99% rename from client_test.go rename to medium_test.go index f6e3e26..30d875f 100644 --- a/client_test.go +++ b/medium_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" ) -// --- MemoryMedium Compatibility Tests --- +// --- MemoryMedium Tests --- func TestClient_NewMemoryMedium_Good(t *testing.T) { medium := NewMemoryMedium() diff --git a/workspace/service.go b/workspace/service.go index ea3179f..50e2ba9 100644 --- a/workspace/service.go +++ b/workspace/service.go @@ -24,6 +24,23 @@ type CryptProvider interface { CreateKeyPair(name, passphrase string) (string, error) } +const ( + WorkspaceCreateAction = "workspace.create" + WorkspaceSwitchAction = "workspace.switch" +) + +// Example: command := WorkspaceCommand{ +// Action: WorkspaceCreateAction, +// Identifier: "alice", +// Password: "pass123", +// } +type WorkspaceCommand struct { + Action string + Identifier string + Password string + WorkspaceID string +} + // Example: service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider}) type Options struct { // Core is the Core runtime used by the service. @@ -168,41 +185,44 @@ func (service *Service) WorkspaceFileSet(workspaceFilePath, content string) erro return service.medium.Write(filePath, content) } -// service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: myCryptProvider}) -// -// createResult := service.HandleIPCEvents(core.New(), map[string]any{ -// "action": "workspace.create", -// "identifier": "alice", -// "password": "pass123", -// }) -// -// switchResult := service.HandleIPCEvents(core.New(), map[string]any{ -// "action": "workspace.switch", -// "workspaceID": "f3f0d7", -// }) -// -// _ = createResult.OK -// _ = switchResult.OK +// Example: result := service.HandleWorkspaceCommand(WorkspaceCommand{ +// Action: WorkspaceCreateAction, +// Identifier: "alice", +// Password: "pass123", +// }) +func (service *Service) HandleWorkspaceCommand(command WorkspaceCommand) core.Result { + switch command.Action { + case WorkspaceCreateAction: + workspaceID, err := service.CreateWorkspace(command.Identifier, command.Password) + if err != nil { + return core.Result{}.New(err) + } + return core.Result{Value: workspaceID, OK: true} + case WorkspaceSwitchAction: + if err := service.SwitchWorkspace(command.WorkspaceID); err != nil { + return core.Result{}.New(err) + } + return core.Result{OK: true} + } + return core.Result{OK: true} +} + +// Example: result := service.HandleIPCEvents(core.New(), map[string]any{ +// "action": WorkspaceSwitchAction, +// "workspaceID": "f3f0d7", +// }) +// HandleIPCEvents preserves the legacy map[string]any payload and still accepts WorkspaceCommand values. func (service *Service) HandleIPCEvents(_ *core.Core, message core.Message) core.Result { switch payload := message.(type) { + case WorkspaceCommand: + return service.HandleWorkspaceCommand(payload) case map[string]any: - action, _ := payload["action"].(string) - switch action { - case "workspace.create": - identifier, _ := payload["identifier"].(string) - password, _ := payload["password"].(string) - workspaceID, err := service.CreateWorkspace(identifier, password) - if err != nil { - return core.Result{}.New(err) - } - return core.Result{Value: workspaceID, OK: true} - case "workspace.switch": - workspaceID, _ := payload["workspaceID"].(string) - if err := service.SwitchWorkspace(workspaceID); err != nil { - return core.Result{}.New(err) - } - return core.Result{OK: true} - } + command := WorkspaceCommand{} + command.Action, _ = payload["action"].(string) + command.Identifier, _ = payload["identifier"].(string) + command.Password, _ = payload["password"].(string) + command.WorkspaceID, _ = payload["workspaceID"].(string) + return service.HandleWorkspaceCommand(command) } return core.Result{OK: true} } diff --git a/workspace/service_test.go b/workspace/service_test.go index a8b19e3..7dd5042 100644 --- a/workspace/service_test.go +++ b/workspace/service_test.go @@ -88,13 +88,13 @@ func TestService_WorkspaceFileSet_TraversalBlocked_Bad(t *testing.T) { require.Error(t, err) } -func TestService_HandleIPCEvents_Good(t *testing.T) { +func TestService_HandleWorkspaceCommand_Good(t *testing.T) { s, _ := newTestService(t) - create := s.HandleIPCEvents(core.New(), map[string]any{ - "action": "workspace.create", - "identifier": "ipc-user", - "password": "pass123", + create := s.HandleWorkspaceCommand(WorkspaceCommand{ + Action: WorkspaceCreateAction, + Identifier: "ipc-user", + Password: "pass123", }) assert.True(t, create.OK) @@ -102,22 +102,40 @@ func TestService_HandleIPCEvents_Good(t *testing.T) { require.True(t, ok) require.NotEmpty(t, workspaceID) - switchResult := s.HandleIPCEvents(core.New(), map[string]any{ - "action": "workspace.switch", - "workspaceID": workspaceID, + switchResult := s.HandleWorkspaceCommand(WorkspaceCommand{ + Action: WorkspaceSwitchAction, + WorkspaceID: workspaceID, }) assert.True(t, switchResult.OK) assert.Equal(t, workspaceID, s.activeWorkspaceID) + legacyCreate := s.HandleIPCEvents(core.New(), map[string]any{ + "action": WorkspaceCreateAction, + "identifier": "legacy-user", + "password": "pass123", + }) + assert.True(t, legacyCreate.OK) + + legacyWorkspaceID, ok := legacyCreate.Value.(string) + require.True(t, ok) + require.NotEmpty(t, legacyWorkspaceID) + + legacySwitch := s.HandleIPCEvents(core.New(), WorkspaceCommand{ + Action: WorkspaceSwitchAction, + WorkspaceID: legacyWorkspaceID, + }) + assert.True(t, legacySwitch.OK) + assert.Equal(t, legacyWorkspaceID, s.activeWorkspaceID) + rejectedLegacySwitch := s.HandleIPCEvents(core.New(), map[string]any{ - "action": "workspace.switch", + "action": WorkspaceSwitchAction, "name": workspaceID, }) assert.False(t, rejectedLegacySwitch.OK) - assert.Equal(t, workspaceID, s.activeWorkspaceID) + assert.Equal(t, legacyWorkspaceID, s.activeWorkspaceID) failedSwitch := s.HandleIPCEvents(core.New(), map[string]any{ - "action": "workspace.switch", + "action": WorkspaceSwitchAction, "workspaceID": "missing", }) assert.False(t, failedSwitch.OK)