Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
213 lines
5.7 KiB
TypeScript
213 lines
5.7 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { ConsentStorage } from './ConsentStorage';
|
|
import type { ConsentConfig, ConsentState } from '../types';
|
|
|
|
describe('ConsentStorage', () => {
|
|
let storage: ConsentStorage;
|
|
const mockConfig: ConsentConfig = {
|
|
apiEndpoint: 'https://api.example.com',
|
|
siteId: 'test-site',
|
|
debug: false,
|
|
consent: {
|
|
rememberDays: 365,
|
|
},
|
|
};
|
|
|
|
const mockConsent: ConsentState = {
|
|
categories: {
|
|
essential: true,
|
|
functional: true,
|
|
analytics: false,
|
|
marketing: false,
|
|
social: false,
|
|
},
|
|
vendors: {},
|
|
timestamp: '2024-01-15T10:00:00.000Z',
|
|
version: '1.0.0',
|
|
};
|
|
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
storage = new ConsentStorage(mockConfig);
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should create storage with site-specific key', () => {
|
|
expect(storage).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('get', () => {
|
|
it('should return null when no consent stored', () => {
|
|
const result = storage.get();
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should return consent when valid data exists', () => {
|
|
storage.set(mockConsent);
|
|
const result = storage.get();
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.categories.essential).toBe(true);
|
|
expect(result?.categories.analytics).toBe(false);
|
|
});
|
|
|
|
it('should return null and clear when version mismatch', () => {
|
|
// Manually set invalid version in storage
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
localStorage.setItem(
|
|
storageKey,
|
|
JSON.stringify({
|
|
version: 'invalid',
|
|
consent: mockConsent,
|
|
signature: 'test',
|
|
})
|
|
);
|
|
|
|
const result = storage.get();
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should return null and clear when signature invalid', () => {
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
localStorage.setItem(
|
|
storageKey,
|
|
JSON.stringify({
|
|
version: '1',
|
|
consent: mockConsent,
|
|
signature: 'invalid-signature',
|
|
})
|
|
);
|
|
|
|
const result = storage.get();
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should return null when JSON is invalid', () => {
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
localStorage.setItem(storageKey, 'invalid-json');
|
|
|
|
const result = storage.get();
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('set', () => {
|
|
it('should store consent in localStorage', () => {
|
|
storage.set(mockConsent);
|
|
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
const stored = localStorage.getItem(storageKey);
|
|
|
|
expect(stored).toBeDefined();
|
|
expect(stored).toContain('"version":"1"');
|
|
});
|
|
|
|
it('should set a cookie for SSR support', () => {
|
|
storage.set(mockConsent);
|
|
|
|
expect(document.cookie).toContain(`bp_consent_${mockConfig.siteId}`);
|
|
});
|
|
|
|
it('should include signature in stored data', () => {
|
|
storage.set(mockConsent);
|
|
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
|
|
|
expect(stored.signature).toBeDefined();
|
|
expect(typeof stored.signature).toBe('string');
|
|
});
|
|
});
|
|
|
|
describe('clear', () => {
|
|
it('should remove consent from localStorage', () => {
|
|
storage.set(mockConsent);
|
|
storage.clear();
|
|
|
|
const result = storage.get();
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should clear the cookie', () => {
|
|
storage.set(mockConsent);
|
|
storage.clear();
|
|
|
|
// Cookie should be cleared (expired)
|
|
expect(document.cookie).toContain('expires=');
|
|
});
|
|
});
|
|
|
|
describe('exists', () => {
|
|
it('should return false when no consent exists', () => {
|
|
expect(storage.exists()).toBe(false);
|
|
});
|
|
|
|
it('should return true when consent exists', () => {
|
|
storage.set(mockConsent);
|
|
expect(storage.exists()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('signature verification', () => {
|
|
it('should detect tampered consent data', () => {
|
|
storage.set(mockConsent);
|
|
|
|
const storageKey = `bp_consent_${mockConfig.siteId}`;
|
|
const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
|
|
|
// Tamper with the data
|
|
stored.consent.categories.marketing = true;
|
|
localStorage.setItem(storageKey, JSON.stringify(stored));
|
|
|
|
const result = storage.get();
|
|
expect(result).toBeNull(); // Signature mismatch should clear
|
|
});
|
|
});
|
|
|
|
describe('debug mode', () => {
|
|
it('should log when debug is enabled', () => {
|
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
|
|
const debugStorage = new ConsentStorage({
|
|
...mockConfig,
|
|
debug: true,
|
|
});
|
|
|
|
debugStorage.set(mockConsent);
|
|
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
consoleSpy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('cookie settings', () => {
|
|
it('should set Secure flag on HTTPS', () => {
|
|
storage.set(mockConsent);
|
|
expect(document.cookie).toContain('Secure');
|
|
});
|
|
|
|
it('should set SameSite=Lax', () => {
|
|
storage.set(mockConsent);
|
|
expect(document.cookie).toContain('SameSite=Lax');
|
|
});
|
|
|
|
it('should set expiration based on rememberDays', () => {
|
|
storage.set(mockConsent);
|
|
expect(document.cookie).toContain('expires=');
|
|
});
|
|
});
|
|
|
|
describe('different sites', () => {
|
|
it('should isolate storage by siteId', () => {
|
|
const storage1 = new ConsentStorage({ ...mockConfig, siteId: 'site-1' });
|
|
const storage2 = new ConsentStorage({ ...mockConfig, siteId: 'site-2' });
|
|
|
|
storage1.set(mockConsent);
|
|
|
|
expect(storage1.exists()).toBe(true);
|
|
expect(storage2.exists()).toBe(false);
|
|
});
|
|
});
|
|
});
|