flnxi-Wu/lib/__tests__/premium-module-service.test.ts
2026-06-02 19:25:48 +08:00

594 lines
19 KiB
TypeScript

jest.mock('@/lib/prisma', () => ({
prisma: {
systemConfig: {
findUnique: jest.fn(),
upsert: jest.fn(),
},
},
}));
jest.mock('../license/fingerprint-service', () => ({
generateFingerprint: jest.fn().mockResolvedValue({
machineId: 'machine-1',
hostname: 'nas-1',
macAddress: 'aa:bb:cc:dd:ee:ff',
instanceId: 'instance-1',
}),
}));
jest.mock('../license/license-service', () => ({
getLocalLicense: jest.fn(),
isLicenseConfigExpired: jest.fn(),
}));
jest.mock('../license/crypto-service', () => ({
encryptLocal: jest.fn((data) => `encrypted:${data}`),
decryptLocal: jest.fn((data) => data.replace('encrypted:', '')),
hashData: jest.fn((data) => (data === 'module-entitlement' ? 'hash' : 'unknown-hash')),
verifySignature: jest.fn(),
}));
jest.mock('../license/request-signature', () => ({
buildSignedLicenseRequest: jest.fn((action, payload) => ({
...payload,
timestamp: 1779753600000,
nonce: 'nonce',
signature: `signature:${action}`,
})),
}));
jest.mock('../license/server-config', () => ({
buildLicenseServerLicenseApiUrl: jest.fn((action) => `https://license.test/api/license/flnxi/${action}`),
getLicenseServerUrl: jest.fn(() => 'https://license.test'),
getLicenseProjectCode: jest.fn(() => 'flnxi'),
}));
import { prisma } from '@/lib/prisma';
import { verifySignature } from '../license/crypto-service';
import { getLocalLicense, isLicenseConfigExpired } from '../license/license-service';
const mockFetch = jest.fn();
global.fetch = mockFetch;
const mockVerifySignature = verifySignature as jest.MockedFunction<typeof verifySignature>;
const mockGetLocalLicense = getLocalLicense as jest.MockedFunction<typeof getLocalLicense>;
const mockIsLicenseConfigExpired = isLicenseConfigExpired as jest.MockedFunction<typeof isLicenseConfigExpired>;
const moduleArtifact = {
type: 'registry-image' as const,
image: 'registry.test/flnxi/premium/intranet-penetration-core:1.0.0',
digest: null,
version: '1.0.0',
};
const limitsModuleArtifact = {
type: 'registry-image' as const,
image: 'registry.test/flnxi/premium/license-limits-core:1.0.0',
digest: null,
version: '1.0.0',
};
function mockPullCredential(moduleId = 'intranet-penetration-core') {
return {
registryHost: 'registry.test',
repository: `flnxi/premium/${moduleId}`,
tag: '1.0.0',
digest: null,
username: 'flnxi-module',
pullToken: `pull-token:${moduleId}`,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
};
}
function mockManifestModule(overrides: Record<string, unknown> = {}) {
return {
id: 'intranet-penetration-core',
minLicenseType: 'PRO',
artifact: moduleArtifact,
sizeBytes: 18,
sha256: 'hash',
entitlementVersion: 1,
...overrides,
};
}
function mockLimitsManifestModule(overrides: Record<string, unknown> = {}) {
return {
id: 'license-limits-core',
minLicenseType: 'FREE',
artifact: limitsModuleArtifact,
sizeBytes: 18,
sha256: 'hash',
entitlementVersion: 1,
limits: { maxUsers: 10, maxMaterials: 100 },
...overrides,
};
}
function mockFreeLicense() {
mockGetLocalLicense.mockResolvedValue({
instanceId: 'instance-1',
licenseKey: 'FLNXI-FREE-TEST',
licenseData: 'encrypted-license',
lastVerified: new Date().toISOString(),
cachedConfig: {
type: 'FREE',
limits: { maxUsers: 10, maxMaterials: 100 },
features: {
customTemplates: false,
systemSettings: false,
departmentIsolation: false,
apiAccess: false,
intranetPenetration: false,
},
expiresAt: null,
leaseExpiresAt: new Date(Date.now() + 60_000).toISOString(),
},
} as any);
mockIsLicenseConfigExpired.mockReturnValue(false);
}
function mockPaidLicense(type: 'STANDARD' | 'PRO' = 'PRO') {
mockGetLocalLicense.mockResolvedValue({
instanceId: 'instance-1',
licenseKey: `FLNXI-${type}-TEST`,
licenseData: 'encrypted-license',
lastVerified: new Date().toISOString(),
cachedConfig: {
type,
limits: { maxUsers: -1, maxMaterials: -1 },
features: {
customTemplates: true,
systemSettings: true,
departmentIsolation: true,
apiAccess: true,
intranetPenetration: type === 'PRO',
},
expiresAt: null,
leaseExpiresAt: new Date(Date.now() + 60_000).toISOString(),
},
} as any);
mockIsLicenseConfigExpired.mockReturnValue(false);
}
describe('premium-module-service', () => {
beforeEach(() => {
jest.clearAllMocks();
mockFetch.mockReset();
mockVerifySignature.mockReturnValue(true);
});
it('免费版也应同步限额授权模块', async () => {
mockFreeLicense();
const manifestExpiresAt = new Date(Date.now() + 60_000).toISOString();
mockFetch
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
manifest: {
version: 1,
licenseType: 'FREE',
instanceId: 'instance-1',
modules: [mockLimitsManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: manifestExpiresAt,
},
signature: 'valid-signature',
}),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
entitlement: {
moduleId: 'license-limits-core',
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: limitsModuleArtifact,
},
}),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
pullCredential: mockPullCredential('license-limits-core'),
}),
});
const { syncPremiumModules } = await import('../license/premium-module-service');
const result = await syncPremiumModules();
expect(result).toEqual({ synced: true });
expect(mockFetch).toHaveBeenNthCalledWith(
1,
'https://license.test/api/license/flnxi/premium-modules',
expect.objectContaining({ method: 'POST' })
);
expect(mockFetch).toHaveBeenNthCalledWith(
2,
'https://license.test/api/license/flnxi/premium-modules/license-limits-core',
expect.objectContaining({ method: 'POST' })
);
expect(mockFetch).toHaveBeenNthCalledWith(
3,
'https://license.test/api/license/flnxi/premium-modules/license-limits-core/pull-token',
expect.objectContaining({ method: 'POST' })
);
expect(JSON.parse(mockFetch.mock.calls[1][1].body).manifestExpiresAt).toBe(manifestExpiresAt);
expect(prisma.systemConfig.upsert).toHaveBeenCalledTimes(3);
});
it('付费版应拉取、验签并加密缓存授权模块清单', async () => {
mockPaidLicense('PRO');
const manifestExpiresAt = new Date(Date.now() + 60_000).toISOString();
mockFetch
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: manifestExpiresAt,
},
signature: 'valid-signature',
}),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
entitlement: {
moduleId: 'intranet-penetration-core',
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: moduleArtifact,
},
}),
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
success: true,
pullCredential: mockPullCredential('intranet-penetration-core'),
}),
});
const { syncPremiumModules } = await import('../license/premium-module-service');
const result = await syncPremiumModules();
expect(result).toEqual({ synced: true });
expect(mockFetch).toHaveBeenNthCalledWith(
1,
'https://license.test/api/license/flnxi/premium-modules',
expect.objectContaining({ method: 'POST' })
);
expect(mockFetch).toHaveBeenNthCalledWith(
2,
'https://license.test/api/license/flnxi/premium-modules/intranet-penetration-core',
expect.objectContaining({ method: 'POST' })
);
expect(mockFetch).toHaveBeenNthCalledWith(
3,
'https://license.test/api/license/flnxi/premium-modules/intranet-penetration-core/pull-token',
expect.objectContaining({ method: 'POST' })
);
expect(JSON.parse(mockFetch.mock.calls[1][1].body).manifestExpiresAt).toBe(manifestExpiresAt);
expect(prisma.systemConfig.upsert).toHaveBeenCalledTimes(3);
});
it('授权模块清单同步网络异常时应返回失败结果而不是抛出', async () => {
mockPaidLicense('PRO');
mockFetch.mockRejectedValue(new Error('network down'));
const { syncPremiumModules } = await import('../license/premium-module-service');
const result = await syncPremiumModules();
expect(result).toEqual({ synced: false, error: 'network down' });
expect(prisma.systemConfig.upsert).not.toHaveBeenCalled();
});
it('缓存清单签名无效时不应视为可用模块', async () => {
mockPaidLicense('PRO');
mockVerifySignature.mockReturnValue(false);
(prisma.systemConfig.findUnique as jest.Mock).mockResolvedValue({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule({ sizeBytes: 4096 })],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'bad-signature',
syncedAt: new Date().toISOString(),
})}`,
});
const { hasPremiumModule } = await import('../license/premium-module-service');
await expect(hasPremiumModule('intranet-penetration-core')).resolves.toBe(false);
});
it('只有清单但缺少本地授权凭证时不应视为可用模块', async () => {
mockPaidLicense('PRO');
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
return Promise.resolve(null);
});
const { hasPremiumModule } = await import('../license/premium-module-service');
await expect(hasPremiumModule('intranet-penetration-core')).resolves.toBe(false);
});
it('清单和本地授权凭证都有效时应视为可用模块', async () => {
mockPaidLicense('PRO');
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
if (where.key === 'premium_module_entitlements') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
entitlements: {
'intranet-penetration-core': {
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: moduleArtifact,
syncedAt: new Date().toISOString(),
},
},
})}`,
});
}
return Promise.resolve(null);
});
const { hasPremiumModule } = await import('../license/premium-module-service');
await expect(hasPremiumModule('intranet-penetration-core')).resolves.toBe(true);
});
it('缓存清单许可证类型和当前许可证不一致时不应视为可用模块', async () => {
mockPaidLicense('STANDARD');
(prisma.systemConfig.findUnique as jest.Mock).mockResolvedValue({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule({ sizeBytes: 4096 })],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
const { hasPremiumModule } = await import('../license/premium-module-service');
await expect(hasPremiumModule('intranet-penetration-core')).resolves.toBe(false);
});
it('限额模块清单和授权凭证都有效时应返回签名限额', async () => {
mockFreeLicense();
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'FREE',
instanceId: 'instance-1',
modules: [mockLimitsManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
if (where.key === 'premium_module_entitlements') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
entitlements: {
'license-limits-core': {
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: limitsModuleArtifact,
syncedAt: new Date().toISOString(),
},
},
})}`,
});
}
return Promise.resolve(null);
});
const { getAuthorizedUsageLimits } = await import('../license/premium-module-service');
await expect(getAuthorizedUsageLimits()).resolves.toEqual({
maxUsers: 10,
maxMaterials: 100,
});
});
it('限额模块缺少有效授权凭证时不应返回限额', async () => {
mockFreeLicense();
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'FREE',
instanceId: 'instance-1',
modules: [mockLimitsManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
return Promise.resolve(null);
});
const { getAuthorizedUsageLimits } = await import('../license/premium-module-service');
await expect(getAuthorizedUsageLimits()).resolves.toBeNull();
});
it('缓存中的短期拉包凭证有效时才返回给模块运行器', async () => {
mockPaidLicense('PRO');
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
if (where.key === 'premium_module_entitlements') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
entitlements: {
'intranet-penetration-core': {
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: moduleArtifact,
pullCredential: mockPullCredential('intranet-penetration-core'),
syncedAt: new Date().toISOString(),
},
},
})}`,
});
}
return Promise.resolve(null);
});
const { getCachedModulePullCredential } = await import('../license/premium-module-service');
await expect(getCachedModulePullCredential('intranet-penetration-core')).resolves.toEqual(
expect.objectContaining({
username: 'flnxi-module',
repository: 'flnxi/premium/intranet-penetration-core',
})
);
});
it('过期的短期拉包凭证不应返回给模块运行器', async () => {
mockPaidLicense('PRO');
(prisma.systemConfig.findUnique as jest.Mock).mockImplementation(({ where }) => {
if (where.key === 'premium_module_manifest') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
manifest: {
version: 1,
licenseType: 'PRO',
instanceId: 'instance-1',
modules: [mockManifestModule()],
issuedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 60_000).toISOString(),
},
signature: 'valid-signature',
syncedAt: new Date().toISOString(),
})}`,
});
}
if (where.key === 'premium_module_entitlements') {
return Promise.resolve({
value: `encrypted:${JSON.stringify({
entitlements: {
'intranet-penetration-core': {
entitlementBase64: 'module-entitlement',
sha256: 'hash',
sizeBytes: 18,
expiresAt: new Date(Date.now() + 60_000).toISOString(),
artifact: moduleArtifact,
pullCredential: {
...mockPullCredential('intranet-penetration-core'),
expiresAt: new Date(Date.now() - 1_000).toISOString(),
},
syncedAt: new Date().toISOString(),
},
},
})}`,
});
}
return Promise.resolve(null);
});
const { getCachedModulePullCredential } = await import('../license/premium-module-service');
await expect(getCachedModulePullCredential('intranet-penetration-core')).resolves.toBeNull();
});
});