From c197f6a7a57d887d67e2f8fe43d7a64a3f004217 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 28 Apr 2026 19:12:21 +0100 Subject: [PATCH] Move license test signers to testsupport --- .../v6/internal/subsystems/registry.json | 16 +++++ internal/api/contract_test.go | 7 ++- internal/api/entitlement_handlers_test.go | 3 +- .../api/license_expiry_integration_test.go | 9 +-- internal/api/license_handlers_test.go | 25 ++++---- .../api/license_limits_test_helpers_test.go | 3 +- internal/api/license_user_limit_test.go | 3 +- .../licensing_handlers_auto_migrate_test.go | 25 ++++---- internal/license/license_test.go | 3 +- internal/license/testing_helpers_test.go | 4 +- pkg/licensing/testsupport/testing_helpers.go | 60 +++++++++++++++++++ 11 files changed, 121 insertions(+), 37 deletions(-) create mode 100644 pkg/licensing/testsupport/testing_helpers.go diff --git a/docs/release-control/v6/internal/subsystems/registry.json b/docs/release-control/v6/internal/subsystems/registry.json index 97c523718..b3ebe2653 100644 --- a/docs/release-control/v6/internal/subsystems/registry.json +++ b/docs/release-control/v6/internal/subsystems/registry.json @@ -2117,6 +2117,22 @@ "pkg/licensing/service_activate_test.go" ] }, + { + "id": "license-test-support", + "label": "license test-support helper proof", + "match_prefixes": [ + "pkg/licensing/testsupport/" + ], + "match_files": [], + "allow_same_subsystem_tests": false, + "test_prefixes": [], + "exact_files": [ + "internal/api/contract_test.go", + "internal/api/license_handlers_test.go", + "internal/api/licensing_handlers_auto_migrate_test.go", + "internal/license/testing_helpers_test.go" + ] + }, { "id": "license-server-transport", "label": "license server transport proof", diff --git a/internal/api/contract_test.go b/internal/api/contract_test.go index 0a2a08e95..097891653 100644 --- a/internal/api/contract_test.go +++ b/internal/api/contract_test.go @@ -45,6 +45,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/pkg/cloudauth" "github.com/rcourtman/pulse-go-rewrite/pkg/extensions" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" "github.com/rcourtman/pulse-go-rewrite/pkg/metrics" "github.com/rcourtman/pulse-go-rewrite/pkg/reporting" "github.com/rs/zerolog" @@ -5517,7 +5518,7 @@ func TestContract_LegacyMigrationGrandfatherFloorFallbackJSONSnapshot(t *testing t.Setenv("PULSE_LICENSE_DEV_MODE", "false") const expectedClientVersion = "6.0.0-rc.1" - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_contract_floor", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -5579,7 +5580,7 @@ func TestContract_LegacyMigrationGrandfatherFloorFallbackJSONSnapshot(t *testing if err != nil { t.Fatalf("new persistence: %v", err) } - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("contract-floor@example.com", pkglicensing.TierPro, 24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("contract-floor@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -6437,7 +6438,7 @@ func TestContract_DemoModeCommercialSurfacePolicy(t *testing.T) { handlers := createTestHandler(t) handlers.SetConfig(&config.Config{DemoMode: true}) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("contract-demo@example.com", pkglicensing.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("contract-demo@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("GenerateLicenseForTesting: %v", err) } diff --git a/internal/api/entitlement_handlers_test.go b/internal/api/entitlement_handlers_test.go index 6dbcc0b6a..79b832529 100644 --- a/internal/api/entitlement_handlers_test.go +++ b/internal/api/entitlement_handlers_test.go @@ -14,6 +14,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/license" "github.com/rcourtman/pulse-go-rewrite/internal/license/entitlements" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) func containsCapability(values []string, key string) bool { @@ -230,7 +231,7 @@ func TestBuildCommercialPosturePayloadWithUsage_CurrentValues(t *testing.T) { func TestHandleCommercialPosture_ActiveLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting( + licenseKey, err := licensetestsupport.GenerateLicenseForTesting( "owner@example.com", pkglicensing.TierPro, 24*time.Hour, diff --git a/internal/api/license_expiry_integration_test.go b/internal/api/license_expiry_integration_test.go index 1d4b90e15..db617acc8 100644 --- a/internal/api/license_expiry_integration_test.go +++ b/internal/api/license_expiry_integration_test.go @@ -11,6 +11,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/config" "github.com/rcourtman/pulse-go-rewrite/internal/license" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) // createTestHandlerWithDir creates a LicenseHandlers and returns the base data @@ -36,7 +37,7 @@ func TestExpiredLicenseBlocksProFeature(t *testing.T) { handler := createTestHandler(t) // Activate a Pro license with a 24h expiry. - licenseKey, err := pkglicensing.GenerateLicenseForTesting("expiry@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("expiry@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -93,7 +94,7 @@ func TestGracePeriodAllowsProFeature(t *testing.T) { handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("grace@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("grace@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -134,7 +135,7 @@ func TestExpiredLicenseFeaturesEndpoint(t *testing.T) { handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("features@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("features@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -278,7 +279,7 @@ func TestLicenseGatedEmptyResponseOnExpiry(t *testing.T) { handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("gated@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("gated@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } diff --git a/internal/api/license_handlers_test.go b/internal/api/license_handlers_test.go index fc66371c9..0648e92ad 100644 --- a/internal/api/license_handlers_test.go +++ b/internal/api/license_handlers_test.go @@ -16,6 +16,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/license" "github.com/rcourtman/pulse-go-rewrite/internal/license/entitlements" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) func createTestHandler(t *testing.T) *LicenseHandlers { @@ -64,7 +65,7 @@ func purchaseReturnJTIFromToken(t *testing.T, handler *LicenseHandlers, token st func issueCheckoutActivationGrant(t *testing.T) string { t.Helper() - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_checkout_success", Tier: "pro_plus", State: "active", @@ -156,7 +157,7 @@ func TestHandleLicenseFeatures_WithActiveLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -245,7 +246,7 @@ func TestHandleLicenseStatus_WithActiveLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -403,7 +404,7 @@ func TestHandleActivateLicense_ExchangesLegacyJWTInStrictV6(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_exchanged", Tier: "pro", PlanKey: tc.planKey, @@ -450,7 +451,7 @@ func TestHandleActivateLicense_ExchangesLegacyJWTInStrictV6(t *testing.T) { t.Setenv("PULSE_LICENSE_SERVER_URL", server.URL) handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("legacy-jwt@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("legacy-jwt@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -502,7 +503,7 @@ func TestHandleActivateLicense_ExchangesLegacyJWTInStrictV6(t *testing.T) { func TestHandleActivateLicense_ClearsCommercialMigrationStateOnNativeActivation(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_v6_native", Tier: "pro", State: "active", @@ -588,7 +589,7 @@ func TestHandleActivateLicense_ClearsCommercialMigrationStateOnNativeActivation( func TestHandleActivateLicense_ActivationKeyClearsStaleLegacyPersistence(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_v6_native", Tier: "pro", State: "active", @@ -644,7 +645,7 @@ func TestHandleActivateLicense_ActivationKeyClearsStaleLegacyPersistence(t *test t.Fatalf("new license persistence: %v", err) } - legacyKey, err := pkglicensing.GenerateLicenseForTesting("legacy-stale@example.com", license.TierPro, 24*time.Hour) + legacyKey, err := licensetestsupport.GenerateLicenseForTesting("legacy-stale@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate stale legacy license: %v", err) } @@ -697,7 +698,7 @@ func TestHandleActivateLicense_ValidKey(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("pro@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("pro@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -1456,7 +1457,7 @@ func TestHandleClearLicense_WithActiveLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -1592,7 +1593,7 @@ func TestRequireLicenseFeature_WithLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } @@ -1741,7 +1742,7 @@ func TestLicenseGatedEmptyResponse_WithLicense(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") handler := createTestHandler(t) - licenseKey, err := pkglicensing.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("test@example.com", license.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } diff --git a/internal/api/license_limits_test_helpers_test.go b/internal/api/license_limits_test_helpers_test.go index b69e3e57f..35ac92299 100644 --- a/internal/api/license_limits_test_helpers_test.go +++ b/internal/api/license_limits_test_helpers_test.go @@ -7,6 +7,7 @@ import ( "time" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) type staticLicenseProvider struct { @@ -27,7 +28,7 @@ func setMaxMonitoredSystemsLicenseForTests(t *testing.T, maxMonitoredSystems int t.Setenv("PULSE_LICENSE_DEV_MODE", "true") service := pkglicensing.NewService() - licenseKey, err := pkglicensing.GenerateLicenseForTesting("limits@example.com", pkglicensing.TierEnterprise, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("limits@example.com", pkglicensing.TierEnterprise, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } diff --git a/internal/api/license_user_limit_test.go b/internal/api/license_user_limit_test.go index be0140003..99fe569d0 100644 --- a/internal/api/license_user_limit_test.go +++ b/internal/api/license_user_limit_test.go @@ -12,6 +12,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/models" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) func setMaxUsersLimitForTests(t *testing.T, limits map[string]int64) { @@ -20,7 +21,7 @@ func setMaxUsersLimitForTests(t *testing.T, limits map[string]int64) { t.Setenv("PULSE_LICENSE_DEV_MODE", "true") service := pkglicensing.NewService() - licenseKey, err := pkglicensing.GenerateLicenseForTesting("users-limit@example.com", pkglicensing.TierPro, 24*time.Hour) + licenseKey, err := licensetestsupport.GenerateLicenseForTesting("users-limit@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("failed to generate test license: %v", err) } diff --git a/internal/api/licensing_handlers_auto_migrate_test.go b/internal/api/licensing_handlers_auto_migrate_test.go index 75d74c4bc..3d49b8fc1 100644 --- a/internal/api/licensing_handlers_auto_migrate_test.go +++ b/internal/api/licensing_handlers_auto_migrate_test.go @@ -14,6 +14,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/monitoring" "github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) type grandfatherFloorSupplementalProvider struct { @@ -46,7 +47,7 @@ func (p *grandfatherFloorSupplementalProvider) settle(count int) { func TestGetTenantComponents_AutoExchangesPersistedLegacyJWT(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_migrated", Tier: "pro", State: "active", @@ -108,7 +109,7 @@ func TestGetTenantComponents_AutoExchangesPersistedLegacyJWT(t *testing.T) { } // Create and persist a legacy test JWT. - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -182,7 +183,7 @@ func TestGetTenantComponents_SkipsExchange_WhenActivationStateExists(t *testing. } // Save a legacy JWT (shouldn't be used since activation state exists). - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -263,7 +264,7 @@ func TestGetTenantComponents_PersistsCommercialMigrationState_WhenAutoExchangeFa t.Fatalf("init default persistence: %v", err) } - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("user@example.com", pkglicensing.TierPro, 365*24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -325,7 +326,7 @@ func TestGetTenantComponents_PersistsCommercialMigrationState_WhenAutoExchangeFa func TestGetTenantComponents_AutoExchangeGrandfathersObservedMonitoredSystems(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_floor_auto", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -377,7 +378,7 @@ func TestGetTenantComponents_AutoExchangeGrandfathersObservedMonitoredSystems(t t.Fatalf("init default persistence: %v", err) } - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("floor-auto@example.com", pkglicensing.TierPro, 24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("floor-auto@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -424,7 +425,7 @@ func TestGetTenantComponents_AutoExchangeGrandfathersObservedMonitoredSystems(t func TestGetTenantComponents_BackfillsGrandfatherFloorAfterRestoreWhenMonitorArrivesLate(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_floor_restore", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -521,7 +522,7 @@ func TestGetTenantComponents_BackfillsGrandfatherFloorAfterRestoreWhenMonitorArr func TestBillingReads_DoNotRestartLegacyGrandfatherReconcileLoop(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_floor_read_only", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -619,7 +620,7 @@ func TestBillingReads_DoNotRestartLegacyGrandfatherReconcileLoop(t *testing.T) { func TestActivateLicenseKey_GrandfathersObservedMonitoredSystemsForLegacyMigration(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_floor_manual", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -669,7 +670,7 @@ func TestActivateLicenseKey_GrandfathersObservedMonitoredSystemsForLegacyMigrati t.Cleanup(handlers.StopAllBackgroundLoops) ctx := context.WithValue(context.Background(), OrgIDContextKey, "default") - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("floor-manual@example.com", pkglicensing.TierPro, 24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("floor-manual@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } @@ -694,7 +695,7 @@ func TestActivateLicenseKey_GrandfathersObservedMonitoredSystemsForLegacyMigrati func TestGetTenantComponents_DelaysGrandfatherFloorUntilSupplementalInventorySettles(t *testing.T) { t.Setenv("PULSE_LICENSE_DEV_MODE", "false") - grantJWT, grantPublicKey, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, grantPublicKey, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "lic_floor_supplemental", Tier: "pro", PlanKey: "legacy_migration_fallback", @@ -746,7 +747,7 @@ func TestGetTenantComponents_DelaysGrandfatherFloorUntilSupplementalInventorySet t.Fatalf("init default persistence: %v", err) } - legacyJWT, err := pkglicensing.GenerateLicenseForTesting("floor-supplemental@example.com", pkglicensing.TierPro, 24*time.Hour) + legacyJWT, err := licensetestsupport.GenerateLicenseForTesting("floor-supplemental@example.com", pkglicensing.TierPro, 24*time.Hour) if err != nil { t.Fatalf("generate test license: %v", err) } diff --git a/internal/license/license_test.go b/internal/license/license_test.go index 4b4f80851..d6963ae9b 100644 --- a/internal/license/license_test.go +++ b/internal/license/license_test.go @@ -15,6 +15,7 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/license/entitlements" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) // init sets dev mode for tests so license validation works without a real public key @@ -610,7 +611,7 @@ func TestServiceActivateReturnsSnapshot(t *testing.T) { if err != nil { t.Fatalf("failed to generate test license: %v", err) } - grantJWT, pub, err := pkglicensing.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ + grantJWT, pub, err := licensetestsupport.GenerateGrantJWTForTesting(pkglicensing.GrantClaims{ LicenseID: "snapshot_test", Email: "snapshot@example.com", Tier: string(TierPro), diff --git a/internal/license/testing_helpers_test.go b/internal/license/testing_helpers_test.go index 3bb4134d9..1e335b10c 100644 --- a/internal/license/testing_helpers_test.go +++ b/internal/license/testing_helpers_test.go @@ -3,9 +3,9 @@ package license import ( "time" - pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" + licensetestsupport "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/testsupport" ) func GenerateLicenseForTesting(email string, tier Tier, expiresIn time.Duration) (string, error) { - return pkglicensing.GenerateLicenseForTesting(email, tier, expiresIn) + return licensetestsupport.GenerateLicenseForTesting(email, tier, expiresIn) } diff --git a/pkg/licensing/testsupport/testing_helpers.go b/pkg/licensing/testsupport/testing_helpers.go new file mode 100644 index 000000000..61d859605 --- /dev/null +++ b/pkg/licensing/testsupport/testing_helpers.go @@ -0,0 +1,60 @@ +// Package testsupport contains license fixtures for cross-package tests. +// +// Production code must not import this package. It intentionally lives outside +// pkg/licensing so release-tag tests can build without exposing test signers on +// the production licensing package API. +package testsupport + +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "time" + + "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" +) + +// GenerateLicenseForTesting creates a legacy JWT-shaped test license. +func GenerateLicenseForTesting(email string, tier licensing.Tier, expiresIn time.Duration) (string, error) { + claims := licensing.Claims{ + LicenseID: fmt.Sprintf("test_%d", time.Now().UnixNano()), + Email: email, + Tier: tier, + IssuedAt: time.Now().Unix(), + } + if expiresIn > 0 { + claims.ExpiresAt = time.Now().Add(expiresIn).Unix() + } + + header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"EdDSA","typ":"JWT"}`)) + payloadBytes, err := json.Marshal(claims) + if err != nil { + return "", fmt.Errorf("marshal test license claims: %w", err) + } + payload := base64.RawURLEncoding.EncodeToString(payloadBytes) + signature := base64.RawURLEncoding.EncodeToString([]byte("test-signature-not-valid")) + + return header + "." + payload + "." + signature, nil +} + +// GenerateGrantJWTForTesting creates a signed grant JWT and returns the +// matching public key so tests can install it before verification. +func GenerateGrantJWTForTesting(claims licensing.GrantClaims) (string, ed25519.PublicKey, error) { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return "", nil, fmt.Errorf("generate grant test key pair: %w", err) + } + + header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"EdDSA"}`)) + payloadBytes, err := json.Marshal(claims) + if err != nil { + return "", nil, fmt.Errorf("marshal grant test claims: %w", err) + } + payload := base64.RawURLEncoding.EncodeToString(payloadBytes) + signedData := []byte(header + "." + payload) + signature := ed25519.Sign(privateKey, signedData) + + return header + "." + payload + "." + base64.RawURLEncoding.EncodeToString(signature), publicKey, nil +}