Organisator kann im selben Browser selbst mitspielen
Ein Aufrufer darf Organisator- und Spieler-Zugang kombinieren (Authorization + X-Player-Token). Berechtigungen prüfen jetzt einzelne Merkmale statt einer Rangstufe. In der Lobby gibt es „Selbst mitspielen?“, Teamlinks funktionieren auch im Organisator-Browser. Online-Status wird direkt nach dem Beitritt aktualisiert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
470b12f232
commit
4152f9c0a6
15 files changed
+248
-59
No files matched your search
+37
-1
@@ -19,12 +19,13 @@ async function setup(overrides = {}) {
|
||||
await new Promise((resolve) => app.server.listen(0, '127.0.0.1', resolve));
|
||||
const base = `http://127.0.0.1:${app.server.address().port}/api`;
|
||||
|
||||
const call = async (method, path, { token, body } = {}) => {
|
||||
const call = async (method, path, { token, playerToken, body } = {}) => {
|
||||
const res = await fetch(base + path, {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
...(token ? { authorization: `Bearer ${token}` } : {}),
|
||||
...(playerToken ? { 'x-player-token': playerToken } : {}),
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
@@ -217,6 +218,41 @@ test('ungenutzte Server werden nach der Frist heruntergefahren', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('Organisator spielt im selben Browser selbst mit (zwei Zugänge)', async () => {
|
||||
const { call, close } = await setup({ requireFullTeams: false });
|
||||
try {
|
||||
const { id, organizerToken: org } = (await call('POST', '/matches', {
|
||||
body: { teamA: 'Alpha', teamB: 'Bravo', mode: 'wingman', format: 'bo1', vetoStart: 'A' },
|
||||
})).body;
|
||||
const codes = (await call('GET', `/matches/${id}`, { token: org })).body.organizer.codes;
|
||||
const me = (await call('POST', `/matches/${id}/join`, { body: { code: codes.A, nickname: 'orga' } })).body;
|
||||
const b1 = (await call('POST', `/matches/${id}/join`, { body: { code: codes.B, nickname: 'b1' } })).body;
|
||||
|
||||
const both = { token: org, playerToken: me.playerToken };
|
||||
const view = (await call('GET', `/matches/${id}`, both)).body;
|
||||
assert.equal(view.viewer.role, 'organizer');
|
||||
assert.equal(view.viewer.isOrganizer, true);
|
||||
assert.equal(view.viewer.isCaptain, true);
|
||||
assert.equal(view.viewer.team, 'A');
|
||||
assert.ok(view.organizer, 'Organisator-Daten bleiben sichtbar');
|
||||
|
||||
assert.equal((await call('POST', `/matches/${id}/ready`, { ...both, body: { ready: true } })).status, 200);
|
||||
await call('POST', `/matches/${id}/ready`, { token: b1.playerToken, body: { ready: true } });
|
||||
assert.equal((await call('POST', `/matches/${id}/veto/start`, both)).status, 200);
|
||||
|
||||
// Nur mit Organisator-Zugang ist er kein Captain und darf nicht bannen
|
||||
const ban = { stepIndex: 0, map: 'de_inferno' };
|
||||
assert.equal((await call('POST', `/matches/${id}/veto/action`, { token: org, body: ban })).status, 403);
|
||||
assert.equal((await call('POST', `/matches/${id}/veto/action`, { ...both, body: ban })).status, 200);
|
||||
|
||||
// Ein fremder Spieler-Token macht niemanden zum Organisator
|
||||
const fake = (await call('GET', `/matches/${id}`, { token: b1.playerToken, playerToken: me.playerToken })).body;
|
||||
assert.equal(fake.viewer.isOrganizer, false);
|
||||
} finally {
|
||||
await close();
|
||||
}
|
||||
});
|
||||
|
||||
test('Spieler verlassen die Lobby, Captain geht über', async () => {
|
||||
const { call, close } = await setup();
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user