Skip to content

Commit 058ef7e

Browse files
committed
refactor(ts-sdk): remove duplicated code across modules
- RunModeType/RunMode: re-export from common/constants.ts in sandbox/types.ts - extractNohupPid: re-export from utils/http.ts in sandbox/utils.ts - sleep: import from utils/retry.ts in model/client.ts instead of private method Addresses code duplication feedback from PR alibaba#492 review.
1 parent b68f228 commit 058ef7e

File tree

6 files changed

+95
-31
lines changed

6 files changed

+95
-31
lines changed

rock/ts-sdk/src/model/client.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,46 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs';
99
import { join } from 'path';
1010
import { tmpdir } from 'os';
1111
import { ModelClient, PollOptions } from './client.js';
12+
import * as retryUtils from '../utils/retry.js';
13+
14+
// Test that ModelClient uses the shared sleep function
15+
describe('sleep function', () => {
16+
test('ModelClient should use shared sleep from utils/retry.ts', async () => {
17+
// This test verifies that ModelClient uses the shared sleep function
18+
// by spying on the retry module's sleep function
19+
20+
const testDir = join(tmpdir(), `model-client-sleep-test-${Date.now()}`);
21+
mkdirSync(testDir, { recursive: true });
22+
const logFile = join(testDir, 'model.log');
23+
24+
try {
25+
// Create log file with request that has index 0
26+
const REQUEST_START_MARKER = '__REQUEST_START__';
27+
const REQUEST_END_MARKER = '__REQUEST_END__';
28+
writeFileSync(logFile, `${REQUEST_START_MARKER}test${REQUEST_END_MARKER}{"index":0}\n`);
29+
30+
const client = new ModelClient({ logFileName: logFile });
31+
32+
// Spy on the sleep function from retry module
33+
const sleepSpy = jest.spyOn(retryUtils, 'sleep').mockResolvedValue();
34+
35+
// popRequest for index 1 should poll and call sleep
36+
// before timing out (we set a very short timeout)
37+
try {
38+
await client.popRequest(1, { timeout: 0.1 });
39+
} catch {
40+
// Expected to timeout
41+
}
42+
43+
// If ModelClient uses shared sleep, the spy should have been called
44+
expect(sleepSpy).toHaveBeenCalled();
45+
46+
sleepSpy.mockRestore();
47+
} finally {
48+
rmSync(testDir, { recursive: true, force: true });
49+
}
50+
});
51+
});
1252

1353
// Test markers (must match client.ts)
1454
const REQUEST_START_MARKER = '__REQUEST_START__';

rock/ts-sdk/src/model/client.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { existsSync, readFileSync, appendFileSync } from 'fs';
66
import { initLogger } from '../logger.js';
77
import { envVars } from '../env_vars.js';
8+
import { sleep } from '../utils/retry.js';
89

910
const logger = initLogger('rock.model.client');
1011

@@ -136,15 +137,15 @@ export class ModelClient {
136137
}
137138

138139
logger.debug(`Last request is not the index ${index} we want, waiting...`);
139-
await this.sleep(1000);
140+
await sleep(1000);
140141
} catch (e) {
141142
// Re-throw abort errors
142143
if (e instanceof Error && e.message.includes('aborted')) {
143144
throw e;
144145
}
145146
// For other errors (like file not found), wait and retry
146147
logger.debug(`Error reading request: ${e}, waiting...`);
147-
await this.sleep(1000);
148+
await sleep(1000);
148149
}
149150
}
150151
}
@@ -172,7 +173,7 @@ export class ModelClient {
172173

173174
if (!existsSync(this.logFile)) {
174175
logger.debug(`Log file ${this.logFile} not found, waiting...`);
175-
await this.sleep(1000);
176+
await sleep(1000);
176177
continue;
177178
}
178179

@@ -181,7 +182,7 @@ export class ModelClient {
181182

182183
if (lines.length === 0) {
183184
logger.debug(`Log file ${this.logFile} is empty, waiting for the first request...`);
184-
await this.sleep(1000);
185+
await sleep(1000);
185186
continue;
186187
}
187188

@@ -251,8 +252,4 @@ export class ModelClient {
251252
const metaJson = JSON.stringify(meta);
252253
return `${RESPONSE_START_MARKER}${lastResponse}${RESPONSE_END_MARKER}${metaJson}\n`;
253254
}
254-
255-
private sleep(ms: number): Promise<void> {
256-
return new Promise((resolve) => setTimeout(resolve, ms));
257-
}
258255
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Tests for Sandbox Types
3+
*
4+
* This test verifies that RunModeType and RunMode are re-exported from
5+
* common/constants.ts rather than duplicated.
6+
*/
7+
8+
import { RunMode, RunModeType } from './types.js';
9+
import { RunMode as ConstantsRunMode, RunModeType as ConstantsRunModeType } from '../common/constants.js';
10+
11+
describe('RunModeType', () => {
12+
test('should be re-exported from constants', () => {
13+
// This verifies that RunModeType in types.ts is the same as in constants.ts
14+
// After refactoring, they should be the same reference (not just equal values)
15+
expect<RunModeType>('normal').toBe('normal');
16+
expect<RunModeType>('nohup').toBe('nohup');
17+
});
18+
19+
test('should be the same reference as constants RunModeType', () => {
20+
// This test will pass once we refactor to re-export from constants
21+
// Currently it may fail if they are different type definitions
22+
const normalMode: RunModeType = 'normal';
23+
const constNormalMode: ConstantsRunModeType = normalMode;
24+
expect(constNormalMode).toBe('normal');
25+
});
26+
});
27+
28+
describe('RunMode', () => {
29+
test('should have correct values', () => {
30+
expect(RunMode.NORMAL).toBe('normal');
31+
expect(RunMode.NOHUP).toBe('nohup');
32+
});
33+
34+
test('should be the same reference as constants RunMode', () => {
35+
// After refactoring, RunMode should be imported from constants
36+
// and re-exported, so they should be the same object
37+
expect(RunMode).toBe(ConstantsRunMode);
38+
});
39+
});

rock/ts-sdk/src/sandbox/types.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,8 @@
22
* Sandbox types
33
*/
44

5-
/**
6-
* Run mode type
7-
*/
8-
export type RunModeType = 'normal' | 'nohup';
9-
10-
/**
11-
* Run mode enum
12-
*/
13-
export const RunMode = {
14-
NORMAL: 'normal' as const,
15-
NOHUP: 'nohup' as const,
16-
};
5+
// Re-export RunModeType and RunMode from constants to avoid duplication
6+
export { RunModeType, RunMode } from '../common/constants.js';
177

188
/**
199
* Speedup type enum

rock/ts-sdk/src/sandbox/utils.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
*/
44

55
import { extractNohupPid } from './utils.js';
6+
import { extractNohupPid as httpExtractNohupPid } from '../utils/http.js';
67
import { PID_PREFIX, PID_SUFFIX } from '../common/constants.js';
78

89
describe('extractNohupPid', () => {
10+
test('should be the same function as in utils/http.ts', () => {
11+
// After refactoring, extractNohupPid should be imported from utils/http.ts
12+
// and re-exported, so they should be the same function reference
13+
expect(extractNohupPid).toBe(httpExtractNohupPid);
14+
});
15+
916
test('should extract PID from valid output', () => {
1017
const output = `some output\n${PID_PREFIX}12345${PID_SUFFIX}\nmore output`;
1118
expect(extractNohupPid(output)).toBe(12345);

rock/ts-sdk/src/sandbox/utils.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,6 @@ export async function arunWithRetry(
120120
}
121121

122122
/**
123-
* Extract nohup PID from output
123+
* Re-export extractNohupPid from utils/http.ts to avoid duplication
124124
*/
125-
export function extractNohupPid(output: string): number | null {
126-
const PID_PREFIX = '__ROCK_PID_START__';
127-
const PID_SUFFIX = '__ROCK_PID_END__';
128-
const pattern = new RegExp(`${PID_PREFIX}(\\d+)${PID_SUFFIX}`);
129-
const match = output.match(pattern);
130-
if (match?.[1]) {
131-
return parseInt(match[1], 10);
132-
}
133-
return null;
134-
}
125+
export { extractNohupPid } from '../utils/http.js';

0 commit comments

Comments
 (0)