Skip to content

Commit 0043a9d

Browse files
authored
chore: refactor tests and add logs on deployment (#1874)
* chore: refactor tests and add logs on deployment * test: fix
1 parent abc06c6 commit 0043a9d

File tree

3 files changed

+132
-82
lines changed

3 files changed

+132
-82
lines changed

content/src/controller/handlers/create-entity-handler.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export async function createEntity(
1919

2020
const logger = logs.getLogger('create-entity')
2121
const entityId: string = context.formData.fields.entityId.value
22+
const userAgent: string = context.request.headers.get('user-agent') ?? 'unknown'
2223

2324
let authChain = extractAuthChain(context.formData.fields)
2425
const ethAddress: EthAddress = authChain ? authChain[0].payload : ''
@@ -52,13 +53,18 @@ export async function createEntity(
5253

5354
if (isSuccessfulDeployment(deploymentResult)) {
5455
metrics.increment('dcl_deployments_endpoint_counter', { kind: 'success' })
56+
logger.info(`POST /entities - Deployment successful`, { entityId, ethAddress, userAgent })
5557
return {
5658
status: 200,
5759
body: { creationTimestamp: deploymentResult }
5860
}
5961
} else if (isInvalidDeployment(deploymentResult)) {
6062
metrics.increment('dcl_deployments_endpoint_counter', { kind: 'validation_error' })
61-
logger.error(`POST /entities - Deployment failed (${deploymentResult.errors.join(',')})`)
63+
logger.error(`POST /entities - Deployment failed (${deploymentResult.errors.join(',')})`, {
64+
entityId,
65+
ethAddress,
66+
userAgent
67+
})
6268
return {
6369
status: 400,
6470
body: { errors: deploymentResult.errors }
@@ -73,7 +79,8 @@ export async function createEntity(
7379
entityId,
7480
authChain: JSON.stringify(authChain),
7581
ethAddress,
76-
signature
82+
signature,
83+
userAgent
7784
})
7885
logger.error(error)
7986
throw error

content/src/service/synchronization/batchDeployer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ export function createBatchDeployerComponent(
161161
)
162162
wasEntityProcessed = true
163163
successfulDeployments.add(entity.entityId)
164+
logs.info(`Synced deployment successful`, {
165+
entityType: entity.entityType,
166+
entityId: entity.entityId,
167+
pointer: entity.pointers?.[0],
168+
servers: elementInMap.servers.join(',')
169+
})
164170
} catch (err: any) {
165171
const errorDescription = err.toString()
166172
logs.warn(`Entity deployment failed`, {
Lines changed: 117 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
import { EntityType } from '@dcl/schemas'
2-
import { sleep } from '@dcl/snapshots-fetcher/dist/utils'
32
import LeakDetector from 'jest-leak-detector'
43
import { createDeployRateLimiter } from '../../../../src/ports/deployRateLimiterComponent'
54
import { makeNoopValidator } from '../../../helpers/service/validations/NoOpValidator'
6-
import { buildDeployData, buildDeployDataAfterEntity } from '../../E2ETestUtils'
5+
import { buildDeployData, buildDeployDataAfterEntity, EntityCombo } from '../../E2ETestUtils'
76
import { TestProgram } from '../../TestProgram'
87
import { createDefaultServer, resetServer } from '../../simpleTestEnvironment'
98

10-
// Short TTLs for testing (in milliseconds — converted to seconds internally by the rate limiter)
9+
// Short TTLs for testing (in milliseconds)
1110
const NORMAL_TTL_MS = 2000
1211
const UNCHANGED_TTL_MS = 5000
1312

1413
describe('Rate limiting E2E', () => {
1514
let server: TestProgram
15+
let currentTime: number
16+
let dateNowSpy: jest.SpyInstance
17+
18+
/**
19+
* Advances the mocked Date.now() by the given milliseconds.
20+
* NodeCache and the deployer's Clock component both rely on Date.now(),
21+
* so this controls TTL expiration without real sleeps.
22+
*/
23+
function advanceTime(ms: number): void {
24+
currentTime += ms
25+
}
1626

1727
/**
1828
* Creates a real rate limiter with short TTLs and assigns its methods
1929
* onto the shared rate limiter object. This works because the deployer
2030
* captures a reference to the same object via Object.assign in the
2131
* test environment setup.
2232
*/
23-
function applyRealRateLimiter() {
33+
function applyRealRateLimiter(): void {
2434
const realRateLimiter = createDeployRateLimiter(
2535
{ logs: server.components.logs },
2636
{
@@ -36,17 +46,20 @@ describe('Rate limiting E2E', () => {
3646

3747
beforeAll(async () => {
3848
server = await createDefaultServer()
39-
// Bypass protocol validation (blockchain checks) but keep server
40-
// validation active — rate limiting is part of server validation
4149
makeNoopValidator(server.components)
4250
})
4351

4452
beforeEach(async () => {
53+
currentTime = Date.now()
54+
dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime)
4555
await resetServer(server)
46-
// Fresh rate limiter per test so cached entries don't leak across tests
4756
applyRealRateLimiter()
4857
})
4958

59+
afterEach(() => {
60+
dateNowSpy.mockRestore()
61+
})
62+
5063
afterAll(async () => {
5164
jest.restoreAllMocks()
5265
const detector = new LeakDetector(server)
@@ -55,101 +68,125 @@ describe('Rate limiting E2E', () => {
5568
expect(await detector.isLeaking()).toBe(false)
5669
})
5770

58-
it('should reject a deployment to the same pointer within the rate limit TTL', async () => {
59-
const { deployData: d1 } = await buildDeployData(['X100,Y100', 'X100,Y101'], {
60-
metadata: { v: 1 }
71+
describe('when deploying to the same pointer within the normal TTL', () => {
72+
let firstDeploy: EntityCombo
73+
let secondDeploy: EntityCombo
74+
75+
beforeEach(async () => {
76+
firstDeploy = await buildDeployData(['X100,Y100', 'X100,Y101'], {
77+
metadata: { v: 1 }
78+
})
79+
await server.deployEntity(firstDeploy.deployData)
80+
81+
secondDeploy = await buildDeployDataAfterEntity(
82+
{ timestamp: currentTime },
83+
['X100,Y100', 'X100,Y101'],
84+
{ metadata: { v: 2 } }
85+
)
6186
})
62-
await server.deployEntity(d1)
6387

64-
// Different entity (different metadata → different entity ID) to the same pointers
65-
const { deployData: d2 } = await buildDeployDataAfterEntity(
66-
{ timestamp: Date.now() },
67-
['X100,Y100', 'X100,Y101'],
68-
{ metadata: { v: 2 } }
69-
)
70-
71-
await expect(server.deployEntity(d2)).rejects.toThrow(/rate limited/i)
88+
it('should reject the deployment', async () => {
89+
await expect(server.deployEntity(secondDeploy.deployData)).rejects.toThrow(/rate limited/i)
90+
})
7291
})
7392

74-
it('should allow a deployment after the normal rate limit TTL expires', async () => {
75-
const { deployData: d1 } = await buildDeployData(['X200,Y200'], {
76-
metadata: { v: 1 }
77-
})
78-
const ts1 = await server.deployEntity(d1)
93+
describe('when deploying after the normal TTL has expired', () => {
94+
let firstTimestamp: number
95+
let secondDeploy: EntityCombo
7996

80-
// Wait for the 2s TTL to expire
81-
await sleep(NORMAL_TTL_MS + 1000)
97+
beforeEach(async () => {
98+
const { deployData: d1 } = await buildDeployData(['X200,Y200'], {
99+
metadata: { v: 1 }
100+
})
101+
firstTimestamp = await server.deployEntity(d1)
82102

83-
const { deployData: d2 } = await buildDeployDataAfterEntity(
84-
{ timestamp: ts1 },
85-
['X200,Y200'],
86-
{ metadata: { v: 2 } }
87-
)
103+
advanceTime(NORMAL_TTL_MS + 1000)
104+
105+
secondDeploy = await buildDeployDataAfterEntity(
106+
{ timestamp: firstTimestamp },
107+
['X200,Y200'],
108+
{ metadata: { v: 2 } }
109+
)
110+
})
88111

89-
// Should succeed — TTL has expired
90-
const ts2 = await server.deployEntity(d2)
91-
expect(ts2).toBeGreaterThan(ts1)
112+
it('should allow the deployment', async () => {
113+
const secondTimestamp: number = await server.deployEntity(secondDeploy.deployData)
114+
expect(secondTimestamp).toBeGreaterThan(firstTimestamp)
115+
})
92116
})
93117

94-
it('should apply a longer rate limit TTL when the same metadata is re-deployed', async () => {
118+
describe('when re-deploying with the same metadata (unchanged content)', () => {
95119
const metadata = { outfit: 'red-shirt', version: 1 }
120+
let secondTimestamp: number
96121

97-
// Deploy 1: initial deployment
98-
const { deployData: d1 } = await buildDeployData(['X300,Y300'], { metadata })
99-
const ts1 = await server.deployEntity(d1)
122+
beforeEach(async () => {
123+
const { deployData: d1 } = await buildDeployData(['X300,Y300'], { metadata })
124+
const firstTimestamp: number = await server.deployEntity(d1)
100125

101-
// Wait for normal TTL (2s) to expire
102-
await sleep(NORMAL_TTL_MS + 1000)
126+
advanceTime(NORMAL_TTL_MS + 1000)
103127

104-
// Deploy 2: same metadata → succeeds, but registers in the unchanged cache (5s TTL)
105-
const { deployData: d2 } = await buildDeployDataAfterEntity(
106-
{ timestamp: ts1 },
107-
['X300,Y300'],
108-
{ metadata }
109-
)
110-
const ts2 = await server.deployEntity(d2)
111-
expect(ts2).toBeGreaterThan(ts1)
128+
const { deployData: d2 } = await buildDeployDataAfterEntity(
129+
{ timestamp: firstTimestamp },
130+
['X300,Y300'],
131+
{ metadata }
132+
)
133+
secondTimestamp = await server.deployEntity(d2)
134+
})
112135

113-
// Wait for normal TTL (2s) to expire, but NOT the unchanged TTL (5s)
114-
await sleep(NORMAL_TTL_MS + 1000)
136+
describe('and the normal TTL has expired but the unchanged TTL has not', () => {
137+
let thirdDeploy: EntityCombo
115138

116-
// Deploy 3: same metadata again → should be rate-limited by the unchanged cache
117-
const { deployData: d3 } = await buildDeployDataAfterEntity(
118-
{ timestamp: ts2 },
119-
['X300,Y300'],
120-
{ metadata }
121-
)
122-
await expect(server.deployEntity(d3)).rejects.toThrow(/rate limited/i)
139+
beforeEach(async () => {
140+
advanceTime(NORMAL_TTL_MS + 1000)
141+
142+
thirdDeploy = await buildDeployDataAfterEntity(
143+
{ timestamp: secondTimestamp },
144+
['X300,Y300'],
145+
{ metadata }
146+
)
147+
})
148+
149+
it('should reject the deployment', async () => {
150+
await expect(server.deployEntity(thirdDeploy.deployData)).rejects.toThrow(/rate limited/i)
151+
})
152+
})
123153
})
124154

125-
it('should allow a deployment with changed metadata even when the unchanged rate limit TTL is active', async () => {
155+
describe('when the unchanged TTL is active from a previous identical deployment', () => {
126156
const metadata = { outfit: 'red-shirt', version: 1 }
157+
let secondTimestamp: number
127158

128-
// Deploy 1: initial deployment
129-
const { deployData: d1 } = await buildDeployData(['X400,Y400'], { metadata })
130-
const ts1 = await server.deployEntity(d1)
159+
beforeEach(async () => {
160+
const { deployData: d1 } = await buildDeployData(['X400,Y400'], { metadata })
161+
const firstTimestamp: number = await server.deployEntity(d1)
131162

132-
// Wait for normal TTL to expire
133-
await sleep(NORMAL_TTL_MS + 1000)
163+
advanceTime(NORMAL_TTL_MS + 1000)
134164

135-
// Deploy 2: same metadata → sets the unchanged cache (5s TTL)
136-
const { deployData: d2 } = await buildDeployDataAfterEntity(
137-
{ timestamp: ts1 },
138-
['X400,Y400'],
139-
{ metadata }
140-
)
141-
const ts2 = await server.deployEntity(d2)
165+
const { deployData: d2 } = await buildDeployDataAfterEntity(
166+
{ timestamp: firstTimestamp },
167+
['X400,Y400'],
168+
{ metadata }
169+
)
170+
secondTimestamp = await server.deployEntity(d2)
142171

143-
// Wait for normal TTL to expire, but NOT unchanged TTL
144-
await sleep(NORMAL_TTL_MS + 1000)
172+
advanceTime(NORMAL_TTL_MS + 1000)
173+
})
145174

146-
// Deploy 3: DIFFERENT metadata → unchanged check doesn't apply → should succeed
147-
const { deployData: d3 } = await buildDeployDataAfterEntity(
148-
{ timestamp: ts2 },
149-
['X400,Y400'],
150-
{ metadata: { outfit: 'blue-shirt', version: 2 } }
151-
)
152-
const ts3 = await server.deployEntity(d3)
153-
expect(ts3).toBeGreaterThan(ts2)
175+
describe('and the new deployment has different metadata', () => {
176+
let thirdDeploy: EntityCombo
177+
178+
beforeEach(async () => {
179+
thirdDeploy = await buildDeployDataAfterEntity(
180+
{ timestamp: secondTimestamp },
181+
['X400,Y400'],
182+
{ metadata: { outfit: 'blue-shirt', version: 2 } }
183+
)
184+
})
185+
186+
it('should allow the deployment', async () => {
187+
const thirdTimestamp: number = await server.deployEntity(thirdDeploy.deployData)
188+
expect(thirdTimestamp).toBeGreaterThan(secondTimestamp)
189+
})
190+
})
154191
})
155192
})

0 commit comments

Comments
 (0)