-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathtest-api.js
More file actions
112 lines (89 loc) · 3.69 KB
/
test-api.js
File metadata and controls
112 lines (89 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**
* Tests: API-Client (public/api.js)
* Fokus: CSRF-Token-Handling, auth:expired-Dispatch-Verhalten
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
// Browser-Globals für Node-Kontext simulieren
global.CustomEvent = class CustomEvent {
constructor(type, init) { this.type = type; this.detail = init?.detail; }
};
let dispatchedEvents = [];
global.window = {
dispatchEvent(e) { dispatchedEvents.push(e); },
addEventListener() {},
};
global.document = { cookie: '' };
// fetch-Mock: wird pro Test überschrieben
let _mockFetch = null;
global.fetch = (...args) => _mockFetch(...args);
function mockResponse(status, body = {}, headers = {}) {
return Promise.resolve({
status,
ok: status >= 200 && status < 300,
headers: {
get(name) { return headers[name] ?? null; },
},
json: () => Promise.resolve(body),
});
}
const { api, auth, ApiError } = await import('./public/api.js');
function setup() {
dispatchedEvents = [];
document.cookie = '';
}
// ─── 401 auf Login-Endpunkt ──────────────────────────────────────────────────
test('auth.login: 401 feuert kein auth:expired', async () => {
setup();
_mockFetch = () => mockResponse(401, { error: 'Invalid credentials.', code: 401 });
await assert.rejects(
() => auth.login('user', 'wrong'),
(err) => {
assert.equal(err.constructor.name, 'ApiError');
assert.equal(err.status, 401);
return true;
},
);
const expired = dispatchedEvents.filter((e) => e.type === 'auth:expired');
assert.equal(expired.length, 0, 'auth:expired darf bei Login-401 nicht gefeuert werden');
});
test('auth.login: 401 wirft ApiError mit status 401', async () => {
setup();
_mockFetch = () => mockResponse(401, { error: 'Invalid credentials.', code: 401 });
let thrownErr;
try {
await auth.login('user', 'wrong');
} catch (e) {
thrownErr = e;
}
assert.ok(thrownErr instanceof ApiError, 'Muss ApiError sein');
assert.equal(thrownErr.status, 401);
});
// ─── 401 auf anderen Endpunkten ─────────────────────────────────────────────
test('api.get: 401 auf geschütztem Endpunkt feuert auth:expired', async () => {
setup();
_mockFetch = () => mockResponse(401, { error: 'Not authenticated.', code: 401 });
await assert.rejects(() => api.get('/tasks'));
const expired = dispatchedEvents.filter((e) => e.type === 'auth:expired');
assert.equal(expired.length, 1, 'auth:expired muss bei 401 auf geschütztem Endpunkt gefeuert werden');
});
test('api.post: 401 auf Logout-Endpunkt feuert auth:expired', async () => {
setup();
_mockFetch = () => mockResponse(401, { error: 'Not authenticated.', code: 401 });
await assert.rejects(() => api.post('/auth/logout', {}));
const expired = dispatchedEvents.filter((e) => e.type === 'auth:expired');
assert.equal(expired.length, 1, 'auth:expired muss bei 401 auf /auth/logout gefeuert werden');
});
// ─── Erfolgreicher Login ─────────────────────────────────────────────────────
test('auth.login: Erfolg speichert csrfToken aus Body', async () => {
setup();
const token = 'abc123def456';
_mockFetch = () => mockResponse(200, {
user: { id: 1, username: 'admin' },
csrfToken: token,
});
const result = await auth.login('admin', 'password');
assert.equal(result.user.username, 'admin');
assert.equal(result.csrfToken, token);
assert.equal(dispatchedEvents.length, 0, 'Kein Event bei erfolgreichem Login');
});