File size: 6,003 Bytes
20f83d9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | /**
* Pure-resolver tests for buildUsageIdentity().
*
* The resolver maps gateway-internal auth state to the four telemetry identity
* fields (auth_kind, principal_id, customer_id, tier). It is intentionally
* pure — no JWT verification, no key hashing of secrets, no I/O — so the
* branch matrix is trivially testable here.
*/
import { describe, expect, test } from 'vitest';
import { buildUsageIdentity, type UsageIdentityInput } from '../_shared/usage-identity';
function baseInput(overrides: Partial<UsageIdentityInput> = {}): UsageIdentityInput {
return {
sessionUserId: null,
isUserApiKey: false,
enterpriseApiKey: null,
widgetKey: null,
clerkOrgId: null,
userApiKeyCustomerRef: null,
tier: null,
planKey: null,
...overrides,
};
}
describe('buildUsageIdentity — plan_key attribution (#4572)', () => {
test('user_api_key carries the resolved planKey', () => {
const ident = buildUsageIdentity(baseInput({
isUserApiKey: true,
sessionUserId: 'u',
tier: 2,
planKey: 'api_business',
}));
expect(ident.plan_key).toBe('api_business');
});
test('enterprise_api_key defaults plan_key to "enterprise" when none supplied', () => {
const ident = buildUsageIdentity(baseInput({ enterpriseApiKey: 'wm_ent_x', tier: 3 }));
expect(ident.auth_kind).toBe('enterprise_api_key');
expect(ident.plan_key).toBe('enterprise');
});
test('anon has null plan_key', () => {
expect(buildUsageIdentity(baseInput()).plan_key).toBeNull();
});
});
describe('buildUsageIdentity — auth_kind branches', () => {
test('user_api_key takes precedence over every other signal', () => {
const ident = buildUsageIdentity(baseInput({
isUserApiKey: true,
sessionUserId: 'user_123',
userApiKeyCustomerRef: 'customer_abc',
enterpriseApiKey: 'should-be-ignored',
widgetKey: 'should-be-ignored',
tier: 2,
}));
expect(ident.auth_kind).toBe('user_api_key');
expect(ident.principal_id).toBe('user_123');
expect(ident.customer_id).toBe('customer_abc');
expect(ident.tier).toBe(2);
});
test('user_api_key falls back to sessionUserId for customer_id when no explicit ref', () => {
const ident = buildUsageIdentity(baseInput({
isUserApiKey: true,
sessionUserId: 'user_123',
tier: 1,
}));
expect(ident.customer_id).toBe('user_123');
});
test('clerk_jwt: customer_id prefers org over user when org is present', () => {
const ident = buildUsageIdentity(baseInput({
sessionUserId: 'user_123',
clerkOrgId: 'org_acme',
tier: 1,
}));
expect(ident.auth_kind).toBe('clerk_jwt');
expect(ident.principal_id).toBe('user_123');
expect(ident.customer_id).toBe('org_acme');
expect(ident.tier).toBe(1);
});
test('clerk_jwt: customer_id falls back to user when no org', () => {
const ident = buildUsageIdentity(baseInput({
sessionUserId: 'user_123',
}));
expect(ident.customer_id).toBe('user_123');
expect(ident.tier).toBe(0);
});
test('enterprise_api_key: principal_id is hashed, not raw', () => {
const ident = buildUsageIdentity(baseInput({
enterpriseApiKey: 'wm_super_secret_key',
tier: 3,
}));
expect(ident.auth_kind).toBe('enterprise_api_key');
expect(ident.principal_id).not.toBe('wm_super_secret_key');
expect(ident.principal_id).toMatch(/^[0-9a-z]+$/);
// Customer is the unmapped sentinel until a real entry is added to ENTERPRISE_KEY_TO_CUSTOMER
expect(ident.customer_id).toBe('enterprise-unmapped');
expect(ident.tier).toBe(3);
});
test('widget_key: customer_id is the widget key itself, principal_id is hashed', () => {
const ident = buildUsageIdentity(baseInput({
widgetKey: 'widget_pub_xyz',
}));
expect(ident.auth_kind).toBe('widget_key');
expect(ident.customer_id).toBe('widget_pub_xyz');
expect(ident.principal_id).not.toBe('widget_pub_xyz');
expect(ident.principal_id).toMatch(/^[0-9a-z]+$/);
expect(ident.tier).toBe(0);
});
test('anon: every field null, tier always zero', () => {
const ident = buildUsageIdentity(baseInput());
expect(ident.auth_kind).toBe('anon');
expect(ident.principal_id).toBeNull();
expect(ident.customer_id).toBeNull();
expect(ident.tier).toBe(0);
});
test('anon: tier coerces to 0 even if input.tier was set (defensive)', () => {
// No identity signal but a leftover tier value should not show up as a mystery free row.
const ident = buildUsageIdentity(baseInput({ tier: 99 }));
expect(ident.tier).toBe(0);
});
});
describe('buildUsageIdentity — tier handling', () => {
test('null tier coerces to 0 for non-anon kinds', () => {
const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: null }));
expect(ident.tier).toBe(0);
});
test('zero tier is preserved (not promoted)', () => {
const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: 0 }));
expect(ident.tier).toBe(0);
});
test('integer tiers pass through unchanged', () => {
for (const t of [0, 1, 2, 3]) {
const ident = buildUsageIdentity(baseInput({ sessionUserId: 'u', tier: t }));
expect(ident.tier).toBe(t);
}
});
});
describe('buildUsageIdentity — secret handling', () => {
test('enterprise key never appears verbatim in any output field', () => {
const secret = 'wm_ent_LEAKY_VALUE_DO_NOT_LOG';
const ident = buildUsageIdentity(baseInput({ enterpriseApiKey: secret }));
expect(JSON.stringify(ident)).not.toContain(secret);
});
test('widget key appears as customer_id (intentional — widget keys are public)', () => {
// Widget keys are embeds installed on third-party sites; treating them as
// customer attribution is the contract documented in usage-identity.ts:73-79.
const ident = buildUsageIdentity(baseInput({ widgetKey: 'widget_public_xyz' }));
expect(ident.customer_id).toBe('widget_public_xyz');
});
});
|