Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions pkg/actionpins/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,53 @@ func TestSpec_Types_ContainerPin(t *testing.T) {
assert.Equal(t, "ghcr.io/some/image@sha256:abc123", pin.PinnedImage, "ContainerPin.PinnedImage field")
}

// TestSpec_Constants_ResolutionErrorType validates the documented ResolutionErrorType constant values.
// Spec table: ResolutionErrorTypeDynamicResolutionFailed="dynamic_resolution_failed",
// ResolutionErrorTypePinNotFound="pin_not_found".
func TestSpec_Constants_ResolutionErrorType(t *testing.T) {
assert.Equal(t, "dynamic_resolution_failed", string(actionpins.ResolutionErrorTypeDynamicResolutionFailed),
"ResolutionErrorTypeDynamicResolutionFailed should equal the documented value")
assert.Equal(t, "pin_not_found", string(actionpins.ResolutionErrorTypePinNotFound),
"ResolutionErrorTypePinNotFound should equal the documented value")
}

// TestSpec_Types_ResolutionFailure validates the documented ResolutionFailure type structure.
// Spec: "Captures an unresolved action-ref pinning event (repo, ref, error type)".
func TestSpec_Types_ResolutionFailure(t *testing.T) {
failure := actionpins.ResolutionFailure{
Repo: "unknown/action",
Ref: "v1",
ErrorType: actionpins.ResolutionErrorTypePinNotFound,
}
assert.Equal(t, "unknown/action", failure.Repo, "ResolutionFailure.Repo field")
assert.Equal(t, "v1", failure.Ref, "ResolutionFailure.Ref field")
assert.Equal(t, actionpins.ResolutionErrorTypePinNotFound, failure.ErrorType, "ResolutionFailure.ErrorType field")
}

// TestSpec_PublicAPI_RecordResolutionFailure validates the documented auditing behavior:
// PinContext.RecordResolutionFailure collects ResolutionFailure events for unresolved pins,
// classified with ResolutionErrorTypePinNotFound when no usable pin is found.
// Spec section "Auditing Resolution Failures".
func TestSpec_PublicAPI_RecordResolutionFailure(t *testing.T) {
var failures []actionpins.ResolutionFailure
ctx := &actionpins.PinContext{
Warnings: make(map[string]bool),
RecordResolutionFailure: func(f actionpins.ResolutionFailure) {
failures = append(failures, f)
},
}

_, err := actionpins.ResolveActionPin("does-not-exist/unknown-action-xyzzy", "v1", ctx)
require.NoError(t, err, "ResolveActionPin should not error even when the pin is unresolved")

require.Len(t, failures, 1, "RecordResolutionFailure should be invoked once for an unresolved pin")
assert.Equal(t, actionpins.ResolutionErrorTypePinNotFound, failures[0].ErrorType,
"unresolved pin with no resolver should be classified as pin_not_found")
assert.Equal(t, "does-not-exist/unknown-action-xyzzy", failures[0].Repo,
"recorded failure should carry the queried repo")
assert.Equal(t, "v1", failures[0].Ref, "recorded failure should carry the queried ref")
}

// TestSpec_ThreadSafety_ConcurrentGetActionPinsByRepo validates that concurrent calls to GetActionPinsByRepo
// are safe after initialization (sync.Once guarantee from the spec).
func TestSpec_ThreadSafety_ConcurrentGetActionPinsByRepo(t *testing.T) {
Expand Down
53 changes: 53 additions & 0 deletions pkg/workflow/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,56 @@ func TestSpec_Engine_DocumentedEnginesRegistered(t *testing.T) {
})
}
}

// TestSpec_Engine_GlobalRegistrySingleton validates the documented thread-safety contract that
// GetGlobalEngineRegistry returns a singleton initialized once at startup.
// Spec ("Thread Safety"): "The GetGlobalEngineRegistry() singleton is initialized once at startup
// and is safe for concurrent reads thereafter."
func TestSpec_Engine_GlobalRegistrySingleton(t *testing.T) {
first := workflow.GetGlobalEngineRegistry()
second := workflow.GetGlobalEngineRegistry()
require.NotNil(t, first, "GetGlobalEngineRegistry() must return a non-nil registry")
assert.Same(t, first, second,
"GetGlobalEngineRegistry() must return the same singleton instance on repeated calls")
}

// TestSpec_Sandbox_Constants validates the documented SandboxType constant values.
// Spec ("Sandbox Constants"): SandboxTypeAWF = "awf", SandboxTypeDefault = "default" (alias for AWF).
func TestSpec_Sandbox_Constants(t *testing.T) {
assert.Equal(t, "awf", string(workflow.SandboxTypeAWF),
"SandboxTypeAWF must equal the documented value")
assert.Equal(t, "default", string(workflow.SandboxTypeDefault),
"SandboxTypeDefault must equal the documented value")
}

// TestSpec_MCPScripts_Constants validates the documented MCP Scripts constants.
// Spec ("MCP Scripts Constants"): MCPScriptsModeHTTP = "http" is the only supported transport mode;
// MCPScriptsDirectory is the runtime directory where MCP scripts files are generated.
func TestSpec_MCPScripts_Constants(t *testing.T) {
assert.Equal(t, "http", workflow.MCPScriptsModeHTTP,
"MCPScriptsModeHTTP must be the documented http transport mode")
assert.NotEmpty(t, workflow.MCPScriptsDirectory,
"MCPScriptsDirectory must be a non-empty runtime directory path")
}

// TestSpec_ActionPinning_ActionMode validates the documented ActionMode alias and DetectActionMode.
// Spec ("Action Pinning"): ActionMode is an "Action reference mode" with DetectActionMode detecting it.
//
// SPEC_MISMATCH: The README documents ActionMode values as `sha`, `tag`, `local`, but the
// implementation defines them as `dev`, `release`, `script`, and `action`. The README also
// describes DetectActionMode as detecting the "action reference mode" from a version string, but
// the implementation ignores the version parameter and instead detects from build/release context
// (the GH_AW_ACTION_MODE override, the release build flag, and GitHub Actions ref/event context).
// This test exercises the actual API and documents the divergence.
func TestSpec_ActionPinning_ActionMode(t *testing.T) {
// SPEC_MISMATCH: documented values (sha/tag/local) do not exist; the real values are these.
assert.Equal(t, "dev", string(workflow.ActionModeDev), "ActionModeDev value")
assert.Equal(t, "release", string(workflow.ActionModeRelease), "ActionModeRelease value")

// DetectActionMode honors the GH_AW_ACTION_MODE override deterministically, independent of the
// (unused) version argument.
t.Setenv("GH_AW_ACTION_MODE", string(workflow.ActionModeRelease))
mode := workflow.DetectActionMode("ignored-version")
assert.Equal(t, workflow.ActionModeRelease, mode,
"DetectActionMode must honor the GH_AW_ACTION_MODE override regardless of the version arg")
}
Loading