Files
tankwar_proj/server/test/contentSecurity.test.js
T
jakciehan d263c7bf48 Merge feature/add_skin into master: resolve all conflicts
- GameGlobal.js: keep upstream SERVER_URL with /ws suffix
- en.js/zh.js: merge both settings.nickname and settings.profile keys
- SettingsScene.js: keep both nickname row and profile button
- server/index.js: merge express app + content security proxy with
  noServer WebSocket mode and path validation
- Add .gitignore for node_modules and .codebuddy
2026-05-12 07:05:20 +08:00

198 lines
5.7 KiB
JavaScript

/**
* contentSecurityService.test.js
* Unit tests for server-side content security service.
*/
const { describe, it, before, after, beforeEach } = require('node:test');
const assert = require('node:assert/strict');
const ContentSecurityService = require('../services/contentSecurityService');
describe('ContentSecurityService', () => {
let service;
before(() => {
service = new ContentSecurityService();
});
after(() => {
// Clean up rate limiter state
service._requestTimestamps = [];
service._requestQueue.forEach(resolve => resolve());
service._requestQueue = [];
service._isProcessingQueue = false;
});
it('should initialize with rate limiter', () => {
assert.ok(service._requestTimestamps);
assert.ok(Array.isArray(service._requestTimestamps));
assert.ok(service._requestQueue);
assert.ok(Array.isArray(service._requestQueue));
});
it('should have checkTextContent method', () => {
assert.strictEqual(typeof service.checkTextContent, 'function');
});
it('should have checkImageContent method', () => {
assert.strictEqual(typeof service.checkImageContent, 'function');
});
it('should reject checkTextContent when token manager is unavailable', async () => {
// Override token manager to be unavailable
if (service.tokenManager) {
service.tokenManager._accessToken = '';
service.tokenManager._isAvailable = false;
}
try {
const result = await service.checkTextContent('test_openid', 'hello', 1);
// When token is unavailable, should return error or risky
assert.ok(result);
assert.strictEqual(result.pass, false);
} catch (e) {
// Also acceptable - throws when service unavailable
assert.ok(e);
}
});
it('should respect rate limiting', async () => {
// Test rate limiter concept
const now = Date.now();
service._requestTimestamps = [];
// Should allow first request
await service._enforceRateLimit();
assert.ok(service._requestTimestamps.length > 0);
// Fill up rate limit
for (let i = 0; i < 5000; i++) {
service._requestTimestamps.push(now);
}
// Should queue when limit reached (returns a pending Promise)
const limited = service._enforceRateLimit();
assert.ok(limited instanceof Promise);
// Clean up: clear the queue to avoid hanging
service._requestQueue.forEach(resolve => resolve());
service._requestQueue = [];
service._requestTimestamps = [];
service._isProcessingQueue = false;
});
});
describe('ViolationService', () => {
let violationService;
before(() => {
const ViolationService = require('../services/violationService');
const AuditLogger = require('../services/auditLogger');
violationService = new ViolationService({ logger: new AuditLogger() });
});
after(() => {
// Clean up any timers using destroy method
if (violationService.destroy) {
violationService.destroy();
}
});
it('should initialize without errors', () => {
assert.ok(violationService);
});
it('should have recordViolation method', () => {
assert.strictEqual(typeof violationService.recordViolation, 'function');
});
it('should have getMuteStatus method', () => {
assert.strictEqual(typeof violationService.getMuteStatus, 'function');
});
it('should record a violation and increment count', () => {
const testUserId = 'test_user_' + Date.now();
violationService.recordViolation({
userId: testUserId,
violationType: 'text_violation',
contentSummary: '测试违规内容',
scene: 2,
});
const status = violationService.getMuteStatus(testUserId);
assert.ok(status);
assert.strictEqual(typeof status.isMuted, 'boolean');
// Check violation count via internal records
const record = violationService._records.get(testUserId);
assert.ok(record);
assert.strictEqual(typeof record.count, 'number');
assert.strictEqual(record.count, 1);
});
it('should apply mute penalty at 3 violations', () => {
const testUserId = 'mute_test_' + Date.now();
// Record 3 violations
for (let i = 0; i < 3; i++) {
violationService.recordViolation({
userId: testUserId,
violationType: 'pornography',
contentSummary: `violation ${i}`,
scene: 2,
});
}
const status = violationService.getMuteStatus(testUserId);
assert.strictEqual(status.isMuted, true);
assert.ok(status.remainingMs > 0);
});
it('should escalate mute at 5 violations', () => {
const testUserId = 'escalate_test_' + Date.now();
// Record 5 violations
for (let i = 0; i < 5; i++) {
violationService.recordViolation({
userId: testUserId,
violationType: 'gambling',
contentSummary: `violation ${i}`,
scene: 2,
});
}
const status = violationService.getMuteStatus(testUserId);
assert.strictEqual(status.isMuted, true);
});
});
describe('ReportService', () => {
let reportService;
before(() => {
const ReportService = require('../services/reportService');
const AuditLogger = require('../services/auditLogger');
reportService = new ReportService({ logger: new AuditLogger() });
});
it('should initialize without errors', () => {
assert.ok(reportService);
});
it('should have submitReport method', () => {
assert.strictEqual(typeof reportService.submitReport, 'function');
});
it('should accept a report submission', () => {
const result = reportService.submitReport({
contentId: 'test_content_' + Date.now(),
targetUserId: 'target_user_123',
contentType: 'chat',
contentSummary: 'test content',
reporterId: 'reporter_user_456',
reason: 'politics',
});
assert.ok(result);
assert.strictEqual(typeof result.success, 'boolean');
});
});