185 lines
7.1 KiB
JavaScript
185 lines
7.1 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { io } from 'socket.io-client';
|
|
import { randomUUID } from 'node:crypto';
|
|
import { fixture } from './helpers.js';
|
|
import { metersBetween, NearbyLocations } from '../src/nearby.js';
|
|
const point = { latitude: 37.5665, longitude: 126.978, accuracy: 20 };
|
|
|
|
test('거리 계산과 동의한 양쪽 좌표, 반경·만료·여러 탭·변경 알림을 검사한다', () => {
|
|
const changes = [],
|
|
locations = new NearbyLocations(100, (ids) => changes.push(ids));
|
|
try {
|
|
assert.equal(metersBetween(point, point), 0);
|
|
assert(
|
|
metersBetween({ latitude: 0, longitude: 179.9999 }, { latitude: 0, longitude: -179.9999 }) <
|
|
30,
|
|
);
|
|
locations.set('a', 'a1', point);
|
|
assert.equal(changes.length, 0, '혼자서는 불필요한 목록 갱신을 하지 않음');
|
|
locations.set('b', 'b1', { ...point, latitude: point.latitude + 0.0004 });
|
|
assert.equal(locations.nearby('a', 'b'), true);
|
|
const before = changes.length;
|
|
locations.set('b', 'b1', { ...point, latitude: point.latitude + 0.0004 });
|
|
assert.equal(changes.length, before, '같은 위치를 갱신해도 목록 재전송 없음');
|
|
locations.set('a', 'a2', point);
|
|
locations.clear('a1');
|
|
assert.equal(locations.nearby('a', 'b'), true);
|
|
locations.clear('a2');
|
|
assert.equal(locations.nearby('a', 'b'), false);
|
|
locations.set('a', 'a1', point);
|
|
locations.points.get('a1').until = Date.now() - 1;
|
|
assert.equal(locations.nearby('a', 'b'), false);
|
|
locations.clear('a1');
|
|
assert(changes.at(-1).includes('b'));
|
|
locations.set('a', 'a1', { ...point, latitude: point.latitude + 0.01 });
|
|
assert.equal(locations.nearby('a', 'b'), false);
|
|
} finally {
|
|
locations.close();
|
|
}
|
|
});
|
|
async function positioned(f, ip) {
|
|
const client = await f.create(ip, false);
|
|
client.socket = io(f.url, {
|
|
transports: ['websocket'],
|
|
reconnection: false,
|
|
auth: { csrfToken: client.csrf },
|
|
extraHeaders: { Origin: f.cfg.appUrl, Cookie: client.cookie, 'X-Forwarded-For': ip },
|
|
});
|
|
await new Promise((resolve, reject) => {
|
|
client.socket.once('connect', resolve);
|
|
client.socket.once('connect_error', reject);
|
|
});
|
|
return client;
|
|
}
|
|
const publish = (client, position) =>
|
|
client.socket.timeout(3000).emitWithAck('location:update', position);
|
|
|
|
test('다른 네트워크도 양쪽 위치 허용 시 발견·전송하고 좌표는 목록과 DB에 공개하지 않는다', async () => {
|
|
const f = await fixture(false),
|
|
clients = [];
|
|
try {
|
|
const a = await positioned(f, '8.8.8.8'),
|
|
b = await positioned(f, '1.1.1.1'),
|
|
c = await positioned(f, '9.9.9.9');
|
|
clients.push(a, b, c);
|
|
const list = async (client) => (await f.call(client, '/discovery/devices')).json().devices;
|
|
assert.equal((await list(a)).length, 0);
|
|
assert.equal((await publish(a, point)).ok, true);
|
|
assert.equal((await list(a)).length, 0);
|
|
assert.equal((await publish(b, { ...point, latitude: point.latitude + 0.0004 })).ok, true);
|
|
assert.equal((await publish(c, { ...point, latitude: point.latitude + 0.01 })).ok, true);
|
|
assert.deepEqual(
|
|
(await list(a)).map((device) => device.id),
|
|
[b.deviceId],
|
|
);
|
|
assert.deepEqual((await list(a))[0].sources, ['nearby']);
|
|
const snapshot = await f.call(a, '/device/snapshot');
|
|
assert(!snapshot.body.includes('latitude') && !snapshot.body.includes('longitude'));
|
|
assert(
|
|
!f.sqlite
|
|
.prepare("SELECT sql FROM sqlite_master WHERE type='table'")
|
|
.all()
|
|
.some((row) => /latitude|longitude/i.test(row.sql)),
|
|
);
|
|
const request = await f.request(a, b, 'https://example.com/nearby');
|
|
assert.equal(request.statusCode, 200, request.body);
|
|
assert.equal(
|
|
(
|
|
await f.call(
|
|
b,
|
|
`/transfer-requests/${request.json().requestId}/accept`,
|
|
'POST',
|
|
{},
|
|
randomUUID(),
|
|
)
|
|
).statusCode,
|
|
200,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await f.call(
|
|
a,
|
|
`/transfers/${request.json().requestId}/complete`,
|
|
'POST',
|
|
{ urls: ['https://example.com/nearby'] },
|
|
randomUUID(),
|
|
)
|
|
).statusCode,
|
|
200,
|
|
);
|
|
b.socket.emit('location:clear');
|
|
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
assert.equal((await list(a)).length, 0);
|
|
assert.equal((await f.request(a, b, 'https://example.com/outside')).statusCode, 403);
|
|
} finally {
|
|
clients.forEach((client) => client.socket.disconnect());
|
|
await f.close();
|
|
}
|
|
});
|
|
test('기기 숨김·방 참여·세션 종료·부정확한 위치와 잘못된 위치를 검사한다', async () => {
|
|
const f = await fixture(false),
|
|
clients = [];
|
|
try {
|
|
const a = await positioned(f, '8.8.8.8'),
|
|
b = await positioned(f, '1.1.1.1');
|
|
clients.push(a, b);
|
|
assert.equal((await publish(a, { ...point, latitude: 91 })).ok, false);
|
|
assert.equal((await publish(a, { ...point, accuracy: 10000 })).code, 'LOCATION_INACCURATE');
|
|
await publish(a, point);
|
|
await publish(b, point);
|
|
await f.call(b, '/device/me', 'PATCH', { discoverable: false });
|
|
assert.equal(f.service.nearby.guests.has(f.service.guest(b.cookie.split('=')[1]).id), false);
|
|
assert.equal((await publish(b, point)).code, 'LOCATION_UNAVAILABLE');
|
|
await f.call(b, '/device/me', 'PATCH', { discoverable: true });
|
|
await f.space(a, b);
|
|
assert.equal((await publish(a, point)).code, 'LOCATION_UNAVAILABLE');
|
|
const d = await positioned(f, '9.9.9.9');
|
|
clients.push(d);
|
|
await publish(d, point);
|
|
assert.equal(
|
|
(await f.call(d, '/discovery/devices')).json().devices.length,
|
|
0,
|
|
'방 안 기기는 외부 위치 목록에 나오지 않음',
|
|
);
|
|
const guestId = f.service.guest(d.cookie.split('=')[1]).id;
|
|
d.socket.disconnect();
|
|
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
assert.equal(f.service.nearby.guests.has(guestId), false);
|
|
} finally {
|
|
clients.forEach((client) => client.socket.disconnect());
|
|
await f.close();
|
|
}
|
|
});
|
|
test('수락 클릭으로 새 탭이 열려 위치가 제거돼도 해당 요청만 완료할 수 있다', async () => {
|
|
const f = await fixture(false),
|
|
clients = [];
|
|
try {
|
|
const a = await positioned(f, '8.8.8.8'),
|
|
b = await positioned(f, '1.1.1.1');
|
|
clients.push(a, b);
|
|
await publish(a, point);
|
|
await publish(b, point);
|
|
const response = await f.request(a, b, 'https://example.com/accepted'),
|
|
id = response.json().requestId;
|
|
b.socket.emit('location:accept', { requestId: id });
|
|
b.socket.emit('location:clear');
|
|
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
assert.equal(
|
|
f.service.nearby.nearby(
|
|
f.service.guest(a.cookie.split('=')[1]).id,
|
|
f.service.guest(b.cookie.split('=')[1]).id,
|
|
),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
(await f.call(b, `/transfer-requests/${id}/accept`, 'POST', {}, randomUUID())).statusCode,
|
|
200,
|
|
);
|
|
assert.equal(f.service.locationAcceptances.has(id), false);
|
|
} finally {
|
|
clients.forEach((client) => client.socket.disconnect());
|
|
await f.close();
|
|
}
|
|
});
|