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
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* 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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user