|
1 | 1 | import assert from 'node:assert/strict' |
2 | | -import { |
| 2 | +import { mkdtempSync } from 'node:fs' |
| 3 | +import { join } from 'node:path' |
| 4 | +import { tmpdir } from 'node:os' |
| 5 | + |
| 6 | +// Isolate test DB in a temp directory so runs are idempotent |
| 7 | +process.env.DATA_DIR = mkdtempSync(join(tmpdir(), 'reviews-test-')) |
| 8 | + |
| 9 | +const { |
3 | 10 | addReview, |
4 | 11 | getReviewsForShader, |
5 | 12 | getAverageRating, |
6 | 13 | getAllShaderRatings, |
7 | | -} from './reviews-db.ts' |
| 14 | +} = await import('./reviews-db.ts') |
8 | 15 |
|
9 | 16 | function runTest(name: string, callback: () => void | Promise<void>) { |
10 | 17 | const result = callback() |
@@ -105,4 +112,72 @@ runTest('addReview handles optional parameters', () => { |
105 | 112 | assert.equal(reviews[0]!.userID, 'user-456') |
106 | 113 | }) |
107 | 114 |
|
| 115 | +runTest('rejects invalid rating (too high)', () => { |
| 116 | + assert.throws(() => addReview(`${testShader}-bad1`, 6), /Rating must be an integer/) |
| 117 | +}) |
| 118 | + |
| 119 | +runTest('rejects invalid rating (zero)', () => { |
| 120 | + assert.throws(() => addReview(`${testShader}-bad2`, 0), /Rating must be an integer/) |
| 121 | +}) |
| 122 | + |
| 123 | +runTest('rejects invalid rating (float)', () => { |
| 124 | + assert.throws(() => addReview(`${testShader}-bad3`, 3.5), /Rating must be an integer/) |
| 125 | +}) |
| 126 | + |
| 127 | +runTest('sanitizes unknown source to web', () => { |
| 128 | + const shader = `${testShader}-badsource` |
| 129 | + addReview(shader, 3, null, 'evil-source') |
| 130 | + const { reviews } = getReviewsForShader(shader) |
| 131 | + assert.equal(reviews[0]!.source, 'web') |
| 132 | +}) |
| 133 | + |
| 134 | +runTest('truncates long comments', () => { |
| 135 | + const shader = `${testShader}-longcomment` |
| 136 | + const longComment = 'x'.repeat(5000) |
| 137 | + addReview(shader, 4, longComment) |
| 138 | + const { reviews } = getReviewsForShader(shader) |
| 139 | + assert.equal(reviews[0]!.comment!.length, 2000) |
| 140 | +}) |
| 141 | + |
| 142 | +runTest('duplicate review from same reviewer token is rejected', () => { |
| 143 | + const shader = `${testShader}-dupe` |
| 144 | + addReview(shader, 4, null, 'web', null, null, '192.168.1.100', 'reviewer-a') |
| 145 | + assert.throws( |
| 146 | + () => addReview(shader, 5, null, 'web', null, null, '198.51.100.2', 'reviewer-a'), |
| 147 | + /already reviewed this shader/, |
| 148 | + ) |
| 149 | +}) |
| 150 | + |
| 151 | +runTest('allows same shader from different reviewer tokens on same IP', () => { |
| 152 | + const shader = `${testShader}-shared-ip` |
| 153 | + const ip = '10.0.0.1' |
| 154 | + addReview(shader, 4, null, 'web', null, null, ip, 'reviewer-a') |
| 155 | + addReview(shader, 5, null, 'web', null, null, ip, 'reviewer-b') |
| 156 | +}) |
| 157 | + |
| 158 | +runTest('allows different shaders from same IP', () => { |
| 159 | + const ip = '10.0.0.1' |
| 160 | + addReview(`${testShader}-diff1`, 4, null, 'web', null, null, ip) |
| 161 | + addReview(`${testShader}-diff2`, 4, null, 'web', null, null, ip) |
| 162 | + // Should not throw — different shaders |
| 163 | +}) |
| 164 | + |
| 165 | +runTest('reviews without IP bypass rate limiting', () => { |
| 166 | + const shader = `${testShader}-noip` |
| 167 | + addReview(shader, 4, null, 'web', null, null, null) |
| 168 | + addReview(shader, 5, null, 'web', null, null, null) |
| 169 | + // Should not throw — no IP to track |
| 170 | +}) |
| 171 | + |
| 172 | +runTest('rate limit rejects 6th review from same IP within window', () => { |
| 173 | + const ip = '172.16.0.1' |
| 174 | + for (let i = 1; i <= 5; i++) { |
| 175 | + addReview(`${testShader}-rl-${i}`, 3, null, 'web', null, null, ip, `reviewer-${i}`) |
| 176 | + } |
| 177 | + assert.throws( |
| 178 | + () => addReview(`${testShader}-rl-6`, 3, null, 'web', null, null, ip, 'reviewer-6'), |
| 179 | + /Too many reviews submitted/, |
| 180 | + ) |
| 181 | +}) |
| 182 | + |
108 | 183 | console.log('reviews-db tests passed') |
0 commit comments