flex-auth/internal/adapters/tenantengine/context_test.go

43 lines
1.2 KiB
Go
Raw Normal View History

FLEX-WP-0008 T03-T04: tenant-engine live-lookup context adapter, close internal/adapters/tenantengine: HTTPClient.LiveRoles() calls tenant-engine's GET /tenants/{id}/roles/live. LiveRolesResult.Available is the load-bearing field -- false on any transport error, non-200, or malformed body, never inferred as zero roles. AttachToContext() writes both tenant_roles and tenant_roles_available into a CheckRequest.Context map; a consuming Rego policy must check tenant_roles_available before trusting tenant_roles. Architectural finding recorded in the workplan: engine.go's Check() has no context-adapter hook, and none of the existing topaz/relationship/rule adapters are wired into cmd/flex-auth either -- they're standalone packages for downstream composition. This adapter is a request-preparation helper a protected system's own request-building code calls before POST /v1/check, not an engine-internal hook, matching that precedent exactly. 9 Go tests; gofmt/vet/build clean; go test ./... green repo-wide. Verified as a real three-service chain: live flex-auth serve + live tenant-engine, created a tenant and granted it a CUS role through the real flex-auth-gated write path, then read it back through this adapter (via a throwaway harness, not committed) -- known tenant: roles=[CUS] available=true; unknown tenant: roles=[] available=false err="status 404". FLEX-WP-0008 closed: T01-T04 all done. tenant-engine's write path is now real end-to-end; any other protected system can pull live tenant capability role context, fail-closed. Remaining open item unchanged from TEN-WP-0003: KEY-WP-0005 (key-cape's IAM Profile core-claims gap). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 23:33:34 +02:00
package tenantengine_test
import (
"testing"
"github.com/netkingdom/flex-auth/internal/adapters/tenantengine"
)
func TestAttachToContextSetsRolesAndAvailability(t *testing.T) {
ctx := tenantengine.AttachToContext(nil, tenantengine.LiveRolesResult{
Roles: []string{"CUS"},
Available: true,
})
if ctx["tenant_roles_available"] != true {
t.Fatalf("tenant_roles_available = %v, want true", ctx["tenant_roles_available"])
}
roles, ok := ctx["tenant_roles"].([]string)
if !ok || len(roles) != 1 || roles[0] != "CUS" {
t.Fatalf("tenant_roles = %v", ctx["tenant_roles"])
}
}
func TestAttachToContextMarksUnavailableOnFailure(t *testing.T) {
ctx := tenantengine.AttachToContext(map[string]any{"existing": "field"}, tenantengine.LiveRolesResult{
Available: false,
})
if ctx["tenant_roles_available"] != false {
t.Fatalf("tenant_roles_available = %v, want false", ctx["tenant_roles_available"])
}
if ctx["existing"] != "field" {
t.Fatal("AttachToContext must not clobber unrelated context fields")
}
}
func TestAttachToContextHandlesNilContext(t *testing.T) {
ctx := tenantengine.AttachToContext(nil, tenantengine.LiveRolesResult{Available: true, Roles: []string{}})
if ctx == nil {
t.Fatal("expected a non-nil map")
}
}