mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
stubs
This commit is contained in:
@@ -1752,6 +1752,44 @@ const app = new Hono<App>({ strict: false })
|
|||||||
c.json([])
|
c.json([])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The room-economy surface the client asks for on entering a room: the room's own
|
||||||
|
// inventory/offers/gift-drop shops and the caller's slice of them. Nothing here is
|
||||||
|
// stored yet, so every one is an empty list — the client reads that as "this room
|
||||||
|
// sells nothing" and renders no shop, where a 404 stalls the room load instead.
|
||||||
|
//
|
||||||
|
// The `/player` and `purchaseCounts` variants are caller-scoped but deliberately
|
||||||
|
// unauthed, matching the `roomConsumable/.../me` stub above: an empty list is the
|
||||||
|
// same answer for every caller, so there's nothing to protect until something
|
||||||
|
// writes here. Gate them when they start returning real data.
|
||||||
|
.get(
|
||||||
|
'/econ/roomInventory/room/:roomId',
|
||||||
|
listRoute('A room’s inventory', 'Empty stub so the client doesn’t 404'),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
'/econ/roomInventory/room/:roomId/player',
|
||||||
|
listRoute('The caller’s inventory in a room', 'Empty stub'),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
'/econ/roomInventoryItemTags/room/:roomId',
|
||||||
|
listRoute('A room’s inventory item tags', 'Empty stub'),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
.get('/econ/roomOffer/room/:roomId', listRoute('A room’s offers', 'Empty stub'), (c) =>
|
||||||
|
c.json([])
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
'/econ/roomOffer/room/:roomId/purchaseCounts',
|
||||||
|
listRoute('Per-offer purchase counts for a room', 'Empty stub'),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
'/econ/roomGiftDropShops/room/:roomId',
|
||||||
|
listRoute('A room’s gift-drop shops', 'Empty stub'),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
|
||||||
// Unlocked consumables. [Authorize]. The consumables the player has bought (from
|
// Unlocked consumables. [Authorize]. The consumables the player has bought (from
|
||||||
// `buyItem`, stored in the `consumable` table), grouped by item into the client's
|
// `buyItem`, stored in the `consumable` table), grouped by item into the client's
|
||||||
// unlocked-consumable DTO. A player who has bought none gets an empty list.
|
// unlocked-consumable DTO. A player who has bought none gets an empty list.
|
||||||
|
|||||||
@@ -635,6 +635,24 @@ describe('econ endpoints', () => {
|
|||||||
expect(await res.json()).toEqual([])
|
expect(await res.json()).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// The room-economy stubs. One table-driven test: they're the same empty-list answer,
|
||||||
|
// and what's worth pinning is that every path the client asks for on room entry is
|
||||||
|
// registered — an unregistered one 404s and stalls the room load.
|
||||||
|
test('the room-economy endpoints all return []', async () => {
|
||||||
|
for (const path of [
|
||||||
|
'/econ/roomInventory/room/92',
|
||||||
|
'/econ/roomInventory/room/92/player',
|
||||||
|
'/econ/roomInventoryItemTags/room/92',
|
||||||
|
'/econ/roomOffer/room/92',
|
||||||
|
'/econ/roomOffer/room/92/purchaseCounts',
|
||||||
|
'/econ/roomGiftDropShops/room/92',
|
||||||
|
]) {
|
||||||
|
const res = await exports.default.fetch(`${ORIGIN}${path}`)
|
||||||
|
expect(res.status, path).toBe(200)
|
||||||
|
expect(await res.json(), path).toEqual([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test('GET /api/consumables/v2/getUnlocked 401s without a token, returns []', async () => {
|
test('GET /api/consumables/v2/getUnlocked 401s without a token, returns []', async () => {
|
||||||
const anon = await exports.default.fetch(`${ORIGIN}/api/consumables/v2/getUnlocked`)
|
const anon = await exports.default.fetch(`${ORIGIN}/api/consumables/v2/getUnlocked`)
|
||||||
expect(anon.status).toBe(401)
|
expect(anon.status).toBe(401)
|
||||||
@@ -2110,6 +2128,12 @@ describe('econ endpoints', () => {
|
|||||||
'GET /api/storefronts/v3/giftdropstore/{id}',
|
'GET /api/storefronts/v3/giftdropstore/{id}',
|
||||||
'GET /api/storefronts/v4/balance/{currencyType}',
|
'GET /api/storefronts/v4/balance/{currencyType}',
|
||||||
'GET /econ/customAvatarItems/v1/owned',
|
'GET /econ/customAvatarItems/v1/owned',
|
||||||
|
'GET /econ/roomGiftDropShops/room/{roomId}',
|
||||||
|
'GET /econ/roomInventory/room/{roomId}',
|
||||||
|
'GET /econ/roomInventory/room/{roomId}/player',
|
||||||
|
'GET /econ/roomInventoryItemTags/room/{roomId}',
|
||||||
|
'GET /econ/roomOffer/room/{roomId}',
|
||||||
|
'GET /econ/roomOffer/room/{roomId}/purchaseCounts',
|
||||||
'POST /api/CampusCard/v1/UpdateAndGetSubscription',
|
'POST /api/CampusCard/v1/UpdateAndGetSubscription',
|
||||||
'POST /api/avatar/v2/gifts/consume',
|
'POST /api/avatar/v2/gifts/consume',
|
||||||
'POST /api/avatar/v2/set',
|
'POST /api/avatar/v2/set',
|
||||||
|
|||||||
@@ -106,14 +106,15 @@ const NULL_CONNECTION_INFO = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The Photon applications the client connects to (`GET /player/connection-info`).
|
* The Photon applications the client connects to (`GET /player/connection-info`).
|
||||||
* Temporary placeholders — move them to wrangler vars before they need to differ per
|
* Hardcoded temporarily — move them to wrangler vars before they need to differ per
|
||||||
* environment. `photonRegion` matches the value `roomInstanceFromRoom` stamps on every
|
* environment (they are per-deployment ids, not secrets: the client receives all three
|
||||||
|
* in the clear). `photonRegion` matches the value `roomInstanceFromRoom` stamps on every
|
||||||
* instance, so the two can't disagree ('us' resolves to us-east1 for QoS).
|
* instance, so the two can't disagree ('us' resolves to us-east1 for QoS).
|
||||||
*/
|
*/
|
||||||
const PHOTON_APPS = {
|
const PHOTON_APPS = {
|
||||||
photonRealtimeAppId: '',
|
photonRealtimeAppId: 'rf-8f322bdb',
|
||||||
photonVoiceAppId: '',
|
photonVoiceAppId: 'rf-6b4682e1',
|
||||||
photonChatAppId: '',
|
photonChatAppId: 'rf-55fae86e',
|
||||||
photonRegion: 'us',
|
photonRegion: 'us',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
@@ -1768,8 +1769,12 @@ const app = new Hono<App>()
|
|||||||
photonAuthToken,
|
photonAuthToken,
|
||||||
...PHOTON_APPS,
|
...PHOTON_APPS,
|
||||||
photonRoomId,
|
photonRoomId,
|
||||||
voiceConnectionInfo: null,
|
// Empty strings rather than null: there's no separate voice server either
|
||||||
voiceServerId: null,
|
// way, and the client's decoder is likelier to accept a missing-value string
|
||||||
|
// than a null on a string field. The presence payload's
|
||||||
|
// NULL_CONNECTION_INFO keeps its nulls — that one never carries credentials.
|
||||||
|
voiceConnectionInfo: '',
|
||||||
|
voiceServerId: '',
|
||||||
experiments: PHOTON_EXPERIMENTS,
|
experiments: PHOTON_EXPERIMENTS,
|
||||||
},
|
},
|
||||||
error: null,
|
error: null,
|
||||||
|
|||||||
@@ -206,8 +206,8 @@ export const ConnectionInfo = z.object({
|
|||||||
photonChatAppId: z.string().describe('Photon Chat application id'),
|
photonChatAppId: z.string().describe('Photon Chat application id'),
|
||||||
photonRegion: z.string().describe('Region id, matching a room instance’s `photonRegion`'),
|
photonRegion: z.string().describe('Region id, matching a room instance’s `photonRegion`'),
|
||||||
photonRoomId: z.string().describe('The caller’s current instance; empty when they’re in none'),
|
photonRoomId: z.string().describe('The caller’s current instance; empty when they’re in none'),
|
||||||
voiceConnectionInfo: z.null().describe('Null — no separate voice server'),
|
voiceConnectionInfo: z.literal('').describe('Empty — no separate voice server'),
|
||||||
voiceServerId: z.null().describe('Null — no separate voice server'),
|
voiceServerId: z.literal('').describe('Empty — no separate voice server'),
|
||||||
experiments: ConnectionExperiments,
|
experiments: ConnectionExperiments,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -935,16 +935,18 @@ describe('auth-gated endpoints', () => {
|
|||||||
value: {
|
value: {
|
||||||
// A signed JWT, not an opaque id — three base64url segments.
|
// A signed JWT, not an opaque id — three base64url segments.
|
||||||
photonAuthToken: expect.stringMatching(/^[\w-]+\.[\w-]+\.[\w-]+$/),
|
photonAuthToken: expect.stringMatching(/^[\w-]+\.[\w-]+\.[\w-]+$/),
|
||||||
photonRealtimeAppId: '',
|
photonRealtimeAppId: 'rf-8f322bdb',
|
||||||
photonVoiceAppId: '',
|
photonVoiceAppId: 'rf-6b4682e1',
|
||||||
photonChatAppId: '',
|
photonChatAppId: 'rf-55fae86e',
|
||||||
// Matches the region every room instance is stamped with.
|
// Matches the region every room instance is stamped with.
|
||||||
photonRegion: 'us',
|
photonRegion: 'us',
|
||||||
// The room the client is told to join has to be the one matchmaking placed
|
// The room the client is told to join has to be the one matchmaking placed
|
||||||
// them in, or they end up alone in a room of their own.
|
// them in, or they end up alone in a room of their own.
|
||||||
photonRoomId: matchmaked.roomInstance.photonRoomId,
|
photonRoomId: matchmaked.roomInstance.photonRoomId,
|
||||||
voiceConnectionInfo: null,
|
// Empty strings, not nulls — unlike the presence payload's connection fields,
|
||||||
voiceServerId: null,
|
// which stay null (they never carry credentials).
|
||||||
|
voiceConnectionInfo: '',
|
||||||
|
voiceServerId: '',
|
||||||
experiments: {
|
experiments: {
|
||||||
networkTransformSyncInterval: 10,
|
networkTransformSyncInterval: 10,
|
||||||
shouldUseUnreliableOnChange: false,
|
shouldUseUnreliableOnChange: false,
|
||||||
@@ -976,9 +978,10 @@ describe('auth-gated endpoints', () => {
|
|||||||
'rn.env': string
|
'rn.env': string
|
||||||
}
|
}
|
||||||
expect(claims.sub).toBe('961')
|
expect(claims.sub).toBe('961')
|
||||||
// Scoped to the realtime app the same response hands out — a placeholder empty
|
// Scoped to the realtime app the same response hands out. Asserted as agreement
|
||||||
// string until PHOTON_APPS moves to wrangler vars, so assert the two agree rather
|
// rather than a pinned literal: PHOTON_APPS is hardcoded until it moves to wrangler
|
||||||
// than pinning the placeholder itself.
|
// vars, and a token minted for a different app than the client is handed is the bug
|
||||||
|
// worth catching here.
|
||||||
expect(claims.aud).toBe(body.value.photonRealtimeAppId)
|
expect(claims.aud).toBe(body.value.photonRealtimeAppId)
|
||||||
expect(claims.exp).toBeGreaterThan(Math.floor(Date.now() / 1000))
|
expect(claims.exp).toBeGreaterThan(Math.floor(Date.now() / 1000))
|
||||||
// The client is built against prod regardless of which environment we run in.
|
// The client is built against prod regardless of which environment we run in.
|
||||||
|
|||||||
@@ -675,3 +675,11 @@ export const PhotonAccessTokenDto = z.object({
|
|||||||
export const PlayerDataDto = z.object({
|
export const PlayerDataDto = z.object({
|
||||||
Data: z.string().describe('Always empty — no per-room player data is stored'),
|
Data: z.string().describe('Always empty — no per-room player data is stored'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `GET /rooms/{roomId}/experience/player` — the caller's per-room experience/progression
|
||||||
|
* entries. Stubbed empty; the element shape is unknown until something stores one.
|
||||||
|
*/
|
||||||
|
export const RoomExperiencePlayer = z
|
||||||
|
.array(z.unknown())
|
||||||
|
.describe('Always empty — no per-room experience is tracked')
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
getFeaturedRooms,
|
getFeaturedRooms,
|
||||||
getHotRooms,
|
getHotRooms,
|
||||||
getInteraction,
|
getInteraction,
|
||||||
|
getOrCreateDormRoom,
|
||||||
getPresence,
|
getPresence,
|
||||||
getPublicRoomsByCreator,
|
getPublicRoomsByCreator,
|
||||||
getRecommendedRooms,
|
getRecommendedRooms,
|
||||||
@@ -96,6 +97,7 @@ import {
|
|||||||
RoomBanEnvelope,
|
RoomBanEnvelope,
|
||||||
RoomDto,
|
RoomDto,
|
||||||
RoomEnvelope,
|
RoomEnvelope,
|
||||||
|
RoomExperiencePlayer,
|
||||||
roomIdParam,
|
roomIdParam,
|
||||||
RoomLookup,
|
RoomLookup,
|
||||||
RoomResultEnvelope,
|
RoomResultEnvelope,
|
||||||
@@ -845,6 +847,31 @@ const app = new Hono<App>()
|
|||||||
ownedRooms
|
ownedRooms
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The caller's own dorm, in the same shape `GET /rooms/{roomId}` serves — the client
|
||||||
|
// renders it with the same code path. Gets-or-creates, exactly as entering a dorm
|
||||||
|
// does (`match`), so a player who has never been to their dorm gets one here rather
|
||||||
|
// than a 404; the id is stable from then on.
|
||||||
|
.get(
|
||||||
|
'/dormroom/me',
|
||||||
|
describeRoute({
|
||||||
|
tags: ['My rooms'],
|
||||||
|
summary: 'The caller’s dorm',
|
||||||
|
description: [
|
||||||
|
'The caller’s personal dorm room, as `GET /rooms/{roomId}` would serve it —',
|
||||||
|
'`SubRooms` re-attached, same DTO. The dorm is provisioned on first access (cloned',
|
||||||
|
'from the seeded template dorm), so this returns a room for any authed caller and',
|
||||||
|
'never 404s; calling it repeatedly returns the same dorm.',
|
||||||
|
].join(' '),
|
||||||
|
security: AUTHED,
|
||||||
|
responses: { 200: json(RoomDto, 'The caller’s dorm'), 401: UNAUTHORIZED_RESPONSE },
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const accountId = await authedAccountId(c)
|
||||||
|
if (accountId === null) return unauthorized(c)
|
||||||
|
return c.json(await getOrCreateDormRoom(c.env.DB, accountId))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Public: the rooms a given account owns that are publicly viewable. No auth —
|
// Public: the rooms a given account owns that are publicly viewable. No auth —
|
||||||
// returns a bare array (empty when the account owns no public rooms).
|
// returns a bare array (empty when the account owns no public rooms).
|
||||||
.get(
|
.get(
|
||||||
@@ -2611,6 +2638,24 @@ const app = new Hono<App>()
|
|||||||
(c) => c.json({ Data: '' })
|
(c) => c.json({ Data: '' })
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The caller's per-room experience/progression. Stub → empty list.
|
||||||
|
.get(
|
||||||
|
'/rooms/:roomId{[0-9]+}/experience/player',
|
||||||
|
describeRoute({
|
||||||
|
tags: ['Rooms'],
|
||||||
|
summary: 'The caller’s per-room experience',
|
||||||
|
description: [
|
||||||
|
'Per-room experience/progression for the calling player. Nothing tracks any yet, so',
|
||||||
|
'this is an empty list — which the client reads as “no progress in this room”, where',
|
||||||
|
'a 404 would stall the room load. No auth, matching `playerdata/me`: the answer is',
|
||||||
|
'the same for every caller until something writes here.',
|
||||||
|
].join(' '),
|
||||||
|
parameters: [roomIdParam],
|
||||||
|
responses: { 200: json(RoomExperiencePlayer, 'An empty list') },
|
||||||
|
}),
|
||||||
|
(c) => c.json([])
|
||||||
|
)
|
||||||
|
|
||||||
// Single room by id. 404 when the room isn't in D1. Ignores the
|
// Single room by id. 404 when the room isn't in D1. Ignores the
|
||||||
// include/unityAsset* query params.
|
// include/unityAsset* query params.
|
||||||
.get(
|
.get(
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ beforeAll(async () => {
|
|||||||
// Seed each room and split its subrooms into the subroom table (mirrors 0007's backfill).
|
// Seed each room and split its subrooms into the subroom table (mirrors 0007's backfill).
|
||||||
for (const r of importRooms) await seedRoomWithSubRooms(env.DB, r as Record<string, unknown>)
|
for (const r of importRooms) await seedRoomWithSubRooms(env.DB, r as Record<string, unknown>)
|
||||||
|
|
||||||
|
// Accounts table (owned by the auth worker) — provisioning a dorm reads the username
|
||||||
|
// to name the room. Seed the player `dormroom/me` provisions a fresh dorm for.
|
||||||
|
await env.DB.prepare(
|
||||||
|
`CREATE TABLE IF NOT EXISTS account (
|
||||||
|
data TEXT NOT NULL,
|
||||||
|
account_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.accountId')) VIRTUAL
|
||||||
|
)`
|
||||||
|
).run()
|
||||||
|
await env.DB.prepare('INSERT OR IGNORE INTO account (data) VALUES (?1)')
|
||||||
|
.bind(JSON.stringify({ accountId: 999, username: 'Dormer' }))
|
||||||
|
.run()
|
||||||
|
|
||||||
// Relationship table (owned by the api worker) — `visitedby/:playerId` reads it to
|
// Relationship table (owned by the api worker) — `visitedby/:playerId` reads it to
|
||||||
// check the caller is a friend of the player whose history they're asking for.
|
// check the caller is a friend of the player whose history they're asking for.
|
||||||
await env.DB.prepare(
|
await env.DB.prepare(
|
||||||
@@ -129,6 +141,14 @@ describe('rooms endpoints', () => {
|
|||||||
expect(body.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163')
|
expect(body.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Stub. Registered (not 404) matters more than the body: the client asks for this on
|
||||||
|
// room entry, and an unregistered path stalls the load rather than erroring visibly.
|
||||||
|
it('GET /rooms/:id/experience/player returns [] for any room', async () => {
|
||||||
|
const res = await SELF.fetch(`${ORIGIN}/rooms/92/experience/player`)
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
it('GET /rooms/:id 404s for a room not in D1', async () => {
|
it('GET /rooms/:id 404s for a room not in D1', async () => {
|
||||||
const res = await SELF.fetch(`${ORIGIN}/rooms/99999`)
|
const res = await SELF.fetch(`${ORIGIN}/rooms/99999`)
|
||||||
expect(res.status).toBe(404)
|
expect(res.status).toBe(404)
|
||||||
@@ -181,6 +201,41 @@ describe('rooms endpoints', () => {
|
|||||||
expect(other).toEqual([])
|
expect(other).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('GET /dormroom/me serves the caller’s own dorm in the room shape', async () => {
|
||||||
|
// No token → 401. Without this the endpoint would hand out (and provision) a dorm
|
||||||
|
// for whichever account a fallback picked.
|
||||||
|
const noAuth = await SELF.fetch(`${ORIGIN}/dormroom/me`)
|
||||||
|
expect(noAuth.status).toBe(401)
|
||||||
|
|
||||||
|
// Account 1 owns the seeded dorm (RoomId 1), served exactly as GET /rooms/1 does —
|
||||||
|
// same DTO, SubRooms re-attached.
|
||||||
|
const res = await SELF.fetch(`${ORIGIN}/dormroom/me`, { headers: await bearer('1') })
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
const dorm = (await res.json()) as {
|
||||||
|
RoomId: number
|
||||||
|
IsDorm: boolean
|
||||||
|
CreatorAccountId: number
|
||||||
|
SubRooms: Array<{ UnitySceneId: string }>
|
||||||
|
}
|
||||||
|
expect(dorm).toMatchObject({ RoomId: 1, IsDorm: true, CreatorAccountId: 1 })
|
||||||
|
expect(dorm.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163')
|
||||||
|
expect(dorm).toEqual(await (await SELF.fetch(`${ORIGIN}/rooms/1`)).json())
|
||||||
|
|
||||||
|
// A player who has never entered their dorm gets one provisioned rather than a
|
||||||
|
// 404, and it belongs to THEM — not the template dorm they were cloned from.
|
||||||
|
const fresh = (await (
|
||||||
|
await SELF.fetch(`${ORIGIN}/dormroom/me`, { headers: await bearer('999') })
|
||||||
|
).json()) as { RoomId: number; IsDorm: boolean; CreatorAccountId: number }
|
||||||
|
expect(fresh).toMatchObject({ IsDorm: true, CreatorAccountId: 999 })
|
||||||
|
expect(fresh.RoomId).not.toBe(1)
|
||||||
|
|
||||||
|
// Idempotent: the second call is the same dorm, not a second one.
|
||||||
|
const again = (await (
|
||||||
|
await SELF.fetch(`${ORIGIN}/dormroom/me`, { headers: await bearer('999') })
|
||||||
|
).json()) as { RoomId: number }
|
||||||
|
expect(again.RoomId).toBe(fresh.RoomId)
|
||||||
|
})
|
||||||
|
|
||||||
// The website's "My rooms" list is a browser calling this worker from another origin,
|
// The website's "My rooms" list is a browser calling this worker from another origin,
|
||||||
// so a response without CORS headers is one the browser throws away — and the page
|
// so a response without CORS headers is one the browser throws away — and the page
|
||||||
// can't tell that apart from the server being down. Pinned on the preflight too: the
|
// can't tell that apart from the server being down. Pinned on the preflight too: the
|
||||||
@@ -580,9 +635,7 @@ describe('rooms endpoints', () => {
|
|||||||
it('GET /rooms/hot?tag=community serves rooms the Coach account did not create', async () => {
|
it('GET /rooms/hot?tag=community serves rooms the Coach account did not create', async () => {
|
||||||
type Feed = { Results: Array<{ Name: string }>; TotalResults: number }
|
type Feed = { Results: Array<{ Name: string }>; TotalResults: number }
|
||||||
const feed = async (): Promise<Feed> =>
|
const feed = async (): Promise<Feed> =>
|
||||||
(await (
|
(await (await SELF.fetch(`${ORIGIN}/rooms/hot?tag=community&skip=0&take=100`)).json()) as Feed
|
||||||
await SELF.fetch(`${ORIGIN}/rooms/hot?tag=community&skip=0&take=100`)
|
|
||||||
).json()) as Feed
|
|
||||||
const names = async (): Promise<string[]> => (await feed()).Results.map((r) => r.Name)
|
const names = async (): Promise<string[]> => (await feed()).Results.map((r) => r.Name)
|
||||||
|
|
||||||
// No room carries a `community` tag, and every seeded room belongs to Coach
|
// No room carries a `community` tag, and every seeded room belongs to Coach
|
||||||
@@ -2911,6 +2964,7 @@ describe('rooms endpoints', () => {
|
|||||||
'DELETE /rooms/{roomId}/subrooms/{subRoomId}',
|
'DELETE /rooms/{roomId}/subrooms/{subRoomId}',
|
||||||
'GET /',
|
'GET /',
|
||||||
'GET /XXXfeaturedrooms/current',
|
'GET /XXXfeaturedrooms/current',
|
||||||
|
'GET /dormroom/me',
|
||||||
'GET /photon_access_token',
|
'GET /photon_access_token',
|
||||||
'GET /rooms',
|
'GET /rooms',
|
||||||
'GET /rooms/base',
|
'GET /rooms/base',
|
||||||
@@ -2926,6 +2980,7 @@ describe('rooms endpoints', () => {
|
|||||||
'GET /rooms/visitedby/{playerId}',
|
'GET /rooms/visitedby/{playerId}',
|
||||||
'GET /rooms/{roomId}',
|
'GET /rooms/{roomId}',
|
||||||
'GET /rooms/{roomId}/bans',
|
'GET /rooms/{roomId}/bans',
|
||||||
|
'GET /rooms/{roomId}/experience/player',
|
||||||
'GET /rooms/{roomId}/interactionby/me',
|
'GET /rooms/{roomId}/interactionby/me',
|
||||||
'GET /rooms/{roomId}/playerdata/me',
|
'GET /rooms/{roomId}/playerdata/me',
|
||||||
'GET /rooms/{roomId}/similar',
|
'GET /rooms/{roomId}/similar',
|
||||||
|
|||||||
Reference in New Issue
Block a user