From eca9b9a033acc8e928d94379dc48cee2dd0a75c6 Mon Sep 17 00:00:00 2001 From: hmmhmmhm Date: Sun, 18 Oct 2026 08:58:26 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=A0=84=EC=B2=B4=20=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=20=EC=A0=90=EA=B2=80=20=EA=B2=B0=EA=B3=BC=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EC=83=81=ED=95=9C=20=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/durableObjects/upstreamRouteHealth.ts | 6 ++++-- .../upstreamRouteHealth.test.ts | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/durableObjects/upstreamRouteHealth.ts b/src/durableObjects/upstreamRouteHealth.ts index 699c2e91..14394710 100644 --- a/src/durableObjects/upstreamRouteHealth.ts +++ b/src/durableObjects/upstreamRouteHealth.ts @@ -1,6 +1,8 @@ /** 내부 바인딩을 통해서만 접근하는 경로별 상태 저장소입니다. */ import { ROUTE_COOLDOWN_MS, + ROUTE_KEYS, + ROUTE_GROUPS, isRouteKey, validRelayCheck, validRouteCheck, @@ -33,10 +35,10 @@ export class UpstreamRouteHealth { if (path === '/checks') { if ( !Array.isArray(input.checks) || - input.checks.length > 20 || + input.checks.length > ROUTE_KEYS.length || !input.checks.every(validRouteCheck) || !Array.isArray(input.relays) || - input.relays.length > 3 || + input.relays.length > ROUTE_GROUPS.length || !input.relays.every(validRelayCheck) ) return new Response(null, { status: 400 }); diff --git a/tests/durableObjects/upstreamRouteHealth.test.ts b/tests/durableObjects/upstreamRouteHealth.test.ts index 75e716e7..ad057365 100644 --- a/tests/durableObjects/upstreamRouteHealth.test.ts +++ b/tests/durableObjects/upstreamRouteHealth.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import { ROUTE_KEYS, ROUTE_GROUPS } from '../../src/utils/routeHealth.js'; import { UpstreamRouteHealth } from '../../src/durableObjects/upstreamRouteHealth.js'; function store() { let value: unknown; @@ -20,6 +21,20 @@ function store() { return { call }; } describe('upstream route durable object', () => { + it('현재 등록된 모든 경로와 중계의 주기 점검 결과를 함께 저장한다', async () => { + const s = store(); + const checkedAt = Date.now(); + const response = await s.call('/checks', { + checks: ROUTE_KEYS.map((key) => ({ key, direct: true, checkedAt })), + relays: ROUTE_GROUPS.map((group) => ({ group, healthy: true, checkedAt })), + }); + expect(response.status).toBe(204); + const snapshot = await (await s.call('/snapshot')).json(); + expect(Object.keys(snapshot.routes)).toEqual([...ROUTE_KEYS]); + expect(Object.keys(snapshot.relays)).toEqual([...ROUTE_GROUPS]); + expect(snapshot.routes['seven-goods']).toMatchObject({ direct: true, checkedAt }); + expect(snapshot.routes['gs25-stock']).toMatchObject({ direct: false, checkedAt }); + }); it('merges partial checks and pins GS stock', async () => { const s = store(); const checkedAt = Date.now(); @@ -127,8 +142,8 @@ describe('durable state edge cases', () => { { checks: [], relays: [{ group: 'dtryx', healthy: 'yes', checkedAt: 1 }] }, { checks: [{ key: 'cu-stock', direct: true, checkedAt: -1 }], relays: [] }, { checks: [{ key: 'cu-stock', direct: true, checkedAt: Date.now() + 90000 }], relays: [] }, - { checks: Array(21).fill({ key: 'cu-stock', direct: true, checkedAt: 1 }), relays: [] }, - { checks: [], relays: Array(4).fill({ group: 'dtryx', healthy: true, checkedAt: 1 }) }, + { checks: Array(ROUTE_KEYS.length + 1).fill({ key: 'cu-stock', direct: true, checkedAt: 1 }), relays: [] }, + { checks: [], relays: Array(ROUTE_GROUPS.length + 1).fill({ group: 'dtryx', healthy: true, checkedAt: 1 }) }, ])('rejects invalid values %j', async (data) => { expect((await store().call('/checks', data)).status).toBe(400); });