mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
fix tests
This commit is contained in:
parent
a75abe1454
commit
88489ea2eb
9 changed files with 27 additions and 28 deletions
|
|
@ -30,8 +30,8 @@ export class SessionResponseImpl implements WebsocketTypes.ISessionResponse {
|
||||||
ServerDispatch.connectionFailed();
|
ServerDispatch.connectionFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
testConnectionSuccessful(serverOptions: number): void {
|
testConnectionSuccessful(supportsHashedPassword: boolean): void {
|
||||||
ServerDispatch.testConnectionSuccessful(serverOptions);
|
ServerDispatch.testConnectionSuccessful(supportsHashedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
testConnectionFailed(): void {
|
testConnectionFailed(): void {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { LoadingState, useKnownHosts, useReduxEffect, useWebClient } from '@app/
|
||||||
import { getHostPort, HostDTO } from '@app/services';
|
import { getHostPort, HostDTO } from '@app/services';
|
||||||
import { ServerDispatch, ServerSelectors, ServerTypes, useAppSelector } from '@app/store';
|
import { ServerDispatch, ServerSelectors, ServerTypes, useAppSelector } from '@app/store';
|
||||||
import { App } from '@app/types';
|
import { App } from '@app/types';
|
||||||
import { passwordSaltSupported } from '@app/websocket';
|
|
||||||
|
|
||||||
export enum TestConnection {
|
export enum TestConnection {
|
||||||
TESTING = 'testing',
|
TESTING = 'testing',
|
||||||
|
|
@ -83,14 +82,13 @@ export function useKnownHostsComponent({
|
||||||
testConnection(selectedHost);
|
testConnection(selectedHost);
|
||||||
}, [selectedHost]);
|
}, [selectedHost]);
|
||||||
|
|
||||||
useReduxEffect<{ serverOptions: number }>(({ payload: { serverOptions } }) => {
|
useReduxEffect<{ supportsHashedPassword: boolean }>(({ payload: { supportsHashedPassword } }) => {
|
||||||
const host = pendingTestRef.current;
|
const host = pendingTestRef.current;
|
||||||
if (!host) {
|
if (!host) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingTestRef.current = null;
|
pendingTestRef.current = null;
|
||||||
|
|
||||||
const supportsHashedPassword = passwordSaltSupported(serverOptions);
|
|
||||||
if (host.id != null && host.supportsHashedPassword !== supportsHashedPassword) {
|
if (host.id != null && host.supportsHashedPassword !== supportsHashedPassword) {
|
||||||
void knownHosts.update(host.id, { supportsHashedPassword });
|
void knownHosts.update(host.id, { supportsHashedPassword });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ describe('Actions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('testConnectionSuccessful', () => {
|
it('testConnectionSuccessful', () => {
|
||||||
expect(Actions.testConnectionSuccessful({ serverOptions: 1 })).toEqual({
|
expect(Actions.testConnectionSuccessful({ supportsHashedPassword: true })).toEqual({
|
||||||
type: Types.TEST_CONNECTION_SUCCESSFUL,
|
type: Types.TEST_CONNECTION_SUCCESSFUL,
|
||||||
payload: { serverOptions: 1 },
|
payload: { supportsHashedPassword: true },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@ describe('Dispatch', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('testConnectionSuccessful dispatches Actions.testConnectionSuccessful()', () => {
|
it('testConnectionSuccessful dispatches Actions.testConnectionSuccessful()', () => {
|
||||||
Dispatch.testConnectionSuccessful(3);
|
Dispatch.testConnectionSuccessful(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledWith(
|
expect(mockDispatch).toHaveBeenCalledWith(
|
||||||
Actions.testConnectionSuccessful({ serverOptions: 3 }),
|
Actions.testConnectionSuccessful({ supportsHashedPassword: true }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ export const Dispatch = {
|
||||||
testConnectionStarted: () => {
|
testConnectionStarted: () => {
|
||||||
store.dispatch(Actions.testConnectionStarted());
|
store.dispatch(Actions.testConnectionStarted());
|
||||||
},
|
},
|
||||||
testConnectionSuccessful: (serverOptions: number) => {
|
testConnectionSuccessful: (supportsHashedPassword: boolean) => {
|
||||||
store.dispatch(Actions.testConnectionSuccessful({ serverOptions }));
|
store.dispatch(Actions.testConnectionSuccessful({ supportsHashedPassword }));
|
||||||
},
|
},
|
||||||
testConnectionFailed: () => {
|
testConnectionFailed: () => {
|
||||||
store.dispatch(Actions.testConnectionFailed());
|
store.dispatch(Actions.testConnectionFailed());
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,11 @@ export const serverSlice = createSlice({
|
||||||
state.testConnectionStatus = 'testing';
|
state.testConnectionStatus = 'testing';
|
||||||
},
|
},
|
||||||
|
|
||||||
// `serverOptions` is typed on the action so `useReduxEffect` subscribers
|
// `supportsHashedPassword` is typed on the action so `useReduxEffect`
|
||||||
// (see useKnownHostsComponent) can read it from the dispatched action —
|
// subscribers (see useKnownHostsComponent) can persist it to the host
|
||||||
// it's deliberately not stored in state since only the lifecycle matters
|
// record in Dexie. It's deliberately not stored in redux state since
|
||||||
// here; the capability bitmask is persisted per-host to Dexie.
|
// only the lifecycle matters here; per-host capability lives in Dexie.
|
||||||
testConnectionSuccessful: (state, _action: PayloadAction<{ serverOptions: number }>) => {
|
testConnectionSuccessful: (state, _action: PayloadAction<{ supportsHashedPassword: boolean }>) => {
|
||||||
state.testConnectionStatus = 'success';
|
state.testConnectionStatus = 'success';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,23 +205,21 @@ describe('WebClient', () => {
|
||||||
expect(MockWS).toHaveBeenCalledWith(expect.stringMatching(/:\/\/server\.example\.com\/servatrice$/));
|
expect(MockWS).toHaveBeenCalledWith(expect.stringMatching(/:\/\/server\.example\.com\/servatrice$/));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches testConnectionSuccessful with serverOptions on ServerIdentification', () => {
|
it('dispatches testConnectionSuccessful with supportsHashedPassword=true when the bit is set', () => {
|
||||||
client.testConnect(target);
|
client.testConnect(target);
|
||||||
const data = buildServerIdentificationMessage({
|
const data = buildServerIdentificationMessage({
|
||||||
serverOptions: Event_ServerIdentification_ServerOptions.SupportsPasswordHash,
|
serverOptions: Event_ServerIdentification_ServerOptions.SupportsPasswordHash,
|
||||||
});
|
});
|
||||||
wsMockInstance.onmessage({ data: data.buffer });
|
wsMockInstance.onmessage({ data: data.buffer });
|
||||||
expect(mockResponse.session.testConnectionSuccessful).toHaveBeenCalledWith(
|
expect(mockResponse.session.testConnectionSuccessful).toHaveBeenCalledWith(true);
|
||||||
Event_ServerIdentification_ServerOptions.SupportsPasswordHash,
|
|
||||||
);
|
|
||||||
expect(wsMockInstance.close).toHaveBeenCalled();
|
expect(wsMockInstance.close).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports success with serverOptions=0 for naked-password servers', () => {
|
it('dispatches testConnectionSuccessful with supportsHashedPassword=false for naked-password servers', () => {
|
||||||
client.testConnect(target);
|
client.testConnect(target);
|
||||||
const data = buildServerIdentificationMessage({ serverOptions: 0 });
|
const data = buildServerIdentificationMessage({ serverOptions: 0 });
|
||||||
wsMockInstance.onmessage({ data: data.buffer });
|
wsMockInstance.onmessage({ data: data.buffer });
|
||||||
expect(mockResponse.session.testConnectionSuccessful).toHaveBeenCalledWith(0);
|
expect(mockResponse.session.testConnectionSuccessful).toHaveBeenCalledWith(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails on protocol-version mismatch instead of reporting success', () => {
|
it('fails on protocol-version mismatch instead of reporting success', () => {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { StatusEnum } from './types/StatusEnum';
|
||||||
import { ProtobufService } from './services/ProtobufService';
|
import { ProtobufService } from './services/ProtobufService';
|
||||||
import { WebSocketService } from './services/WebSocketService';
|
import { WebSocketService } from './services/WebSocketService';
|
||||||
import { buildWebSocketUrl } from './utils/buildWebSocketUrl';
|
import { buildWebSocketUrl } from './utils/buildWebSocketUrl';
|
||||||
|
import { passwordSaltSupported } from './utils/passwordHasher';
|
||||||
|
|
||||||
export class WebClient {
|
export class WebClient {
|
||||||
private static _instance: WebClient | null = null;
|
private static _instance: WebClient | null = null;
|
||||||
|
|
@ -96,10 +97,12 @@ export class WebClient {
|
||||||
this.testSocket = socket;
|
this.testSocket = socket;
|
||||||
|
|
||||||
// "Green" means reachable AND speaking a compatible Cockatrice protocol.
|
// "Green" means reachable AND speaking a compatible Cockatrice protocol.
|
||||||
// Waiting for Event_ServerIdentification lets us carry serverOptions back
|
// Waiting for Event_ServerIdentification lets us read the hashed-password
|
||||||
// to the UI so naked-password hosts can be distinguished without a login.
|
// capability before the user ever logs in. The bitmask is resolved here
|
||||||
|
// (the websocket layer owns protocol details) so downstream consumers
|
||||||
|
// receive a domain-level boolean instead of a raw integer.
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
const resolve = (ok: boolean, serverOptions = 0): void => {
|
const resolve = (ok: boolean, supportsHashedPassword = false): void => {
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +112,7 @@ export class WebClient {
|
||||||
// already taken over and we'd race a stale result into its pending-ref.
|
// already taken over and we'd race a stale result into its pending-ref.
|
||||||
if (this.testSocket === socket) {
|
if (this.testSocket === socket) {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
this.response.session.testConnectionSuccessful(serverOptions);
|
this.response.session.testConnectionSuccessful(supportsHashedPassword);
|
||||||
} else {
|
} else {
|
||||||
this.response.session.testConnectionFailed();
|
this.response.session.testConnectionFailed();
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +138,7 @@ export class WebClient {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(true, ident.serverOptions);
|
resolve(true, passwordSaltSupported(ident.serverOptions));
|
||||||
} catch {
|
} catch {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export interface ISessionResponse {
|
||||||
loginSuccessful(options: LoginSuccessContext): void;
|
loginSuccessful(options: LoginSuccessContext): void;
|
||||||
loginFailed(): void;
|
loginFailed(): void;
|
||||||
connectionFailed(): void;
|
connectionFailed(): void;
|
||||||
testConnectionSuccessful(serverOptions: number): void;
|
testConnectionSuccessful(supportsHashedPassword: boolean): void;
|
||||||
testConnectionFailed(): void;
|
testConnectionFailed(): void;
|
||||||
updateBuddyList(buddyList: ServerInfo_User[]): void;
|
updateBuddyList(buddyList: ServerInfo_User[]): void;
|
||||||
addToBuddyList(user: ServerInfo_User): void;
|
addToBuddyList(user: ServerInfo_User): void;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue