Use direct SUB2API admin API flow
This commit is contained in:
@@ -73,6 +73,8 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
};
|
||||
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
||||
function normalizePanelMode(value) { return value === 'sub2api' ? 'sub2api' : 'cpa'; }
|
||||
function normalizeSignupMethod(value = '') { return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email'; }
|
||||
function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); }
|
||||
function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
|
||||
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; }
|
||||
function normalizeAutoRunDelayMinutes(value) { return Number(value) || 30; }
|
||||
@@ -163,6 +165,8 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
};
|
||||
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
||||
function normalizePanelMode(value) { return value === 'sub2api' ? 'sub2api' : 'cpa'; }
|
||||
function normalizeSignupMethod(value = '') { return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email'; }
|
||||
function resolveSignupMethod(state = {}) { return normalizeSignupMethod(state?.signupMethod); }
|
||||
function normalizeLocalCpaStep9Mode(value) { return value === 'bypass' ? 'bypass' : 'submit'; }
|
||||
function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Number(value) || 0; }
|
||||
function normalizeAutoRunDelayMinutes(value) { return Number(value) || 30; }
|
||||
|
||||
@@ -16,10 +16,24 @@ test('panel bridge module exposes a factory', () => {
|
||||
assert.equal(typeof api?.createPanelBridge, 'function');
|
||||
});
|
||||
|
||||
test('panel bridge requests oauth url with step 7 log label payload', () => {
|
||||
function loadPanelBridgeWithSub2Api() {
|
||||
const sub2apiSource = fs.readFileSync('background/sub2api-api.js', 'utf8');
|
||||
const source = fs.readFileSync('background/panel-bridge.js', 'utf8');
|
||||
assert.match(source, /logStep:\s*7/);
|
||||
assert.doesNotMatch(source, /logStep:\s*6/);
|
||||
return new Function('self', `${sub2apiSource}\n${source}; return self.MultiPageBackgroundPanelBridge;`)({});
|
||||
}
|
||||
|
||||
function createSub2ApiResponse(payload, status = 200) {
|
||||
return {
|
||||
ok: status >= 200 && status < 300,
|
||||
status,
|
||||
text: async () => JSON.stringify(payload),
|
||||
};
|
||||
}
|
||||
|
||||
test('panel bridge requests SUB2API oauth url via direct API', () => {
|
||||
const source = fs.readFileSync('background/panel-bridge.js', 'utf8');
|
||||
assert.match(source, /generateOpenAiAuthUrl/);
|
||||
assert.doesNotMatch(source, /REQUEST_OAUTH_URL/);
|
||||
});
|
||||
|
||||
test('panel bridge can request codex2api oauth url via protocol', async () => {
|
||||
@@ -117,16 +131,63 @@ test('panel bridge can request cpa oauth url via management api', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('panel bridge forwards SUB2API account priority when requesting oauth url', async () => {
|
||||
const source = fs.readFileSync('background/panel-bridge.js', 'utf8');
|
||||
const sentMessages = [];
|
||||
test('panel bridge can request SUB2API oauth url without opening the admin page', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const fetchCalls = [];
|
||||
globalThis.fetch = async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
const body = options.body ? JSON.parse(options.body) : null;
|
||||
fetchCalls.push({ path: parsed.pathname, search: parsed.search, method: options.method || 'GET', body });
|
||||
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundPanelBridge;`)({});
|
||||
if (parsed.pathname === '/api/v1/auth/login') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: { access_token: 'admin-token' },
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/groups/all') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: [{ id: 5, name: 'codex', platform: 'openai' }],
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/proxies/all') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: [{
|
||||
id: 7,
|
||||
name: 'shadowrocket',
|
||||
protocol: 'socks5',
|
||||
host: '127.0.0.1',
|
||||
port: 1080,
|
||||
status: 'active',
|
||||
}],
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/openai/generate-auth-url') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: {
|
||||
auth_url: 'https://auth.openai.com/authorize?state=oauth-state',
|
||||
session_id: 'session-123',
|
||||
state: 'oauth-state',
|
||||
},
|
||||
});
|
||||
}
|
||||
return createSub2ApiResponse({ code: 1, message: `unexpected path ${parsed.pathname}` }, 404);
|
||||
};
|
||||
|
||||
const tabCreates = [];
|
||||
const sentMessages = [];
|
||||
const api = loadPanelBridgeWithSub2Api();
|
||||
const bridge = api.createPanelBridge({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
create: async () => ({ id: 72 }),
|
||||
create: async (payload) => {
|
||||
tabCreates.push(payload);
|
||||
return { id: 72 };
|
||||
},
|
||||
},
|
||||
},
|
||||
closeConflictingTabsForSource: async () => {},
|
||||
@@ -137,11 +198,7 @@ test('panel bridge forwards SUB2API account priority when requesting oauth url',
|
||||
rememberSourceLastUrl: async () => {},
|
||||
sendToContentScript: async (sourceName, message, options) => {
|
||||
sentMessages.push({ sourceName, message, options });
|
||||
return {
|
||||
oauthUrl: 'https://auth.openai.com/authorize?state=oauth-state',
|
||||
sub2apiSessionId: 'session-123',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
};
|
||||
return {};
|
||||
},
|
||||
sendToContentScriptResilient: async () => ({}),
|
||||
waitForTabUrlFamily: async () => ({ id: 72 }),
|
||||
@@ -149,16 +206,30 @@ test('panel bridge forwards SUB2API account priority when requesting oauth url',
|
||||
SUB2API_STEP1_RESPONSE_TIMEOUT_MS: 90000,
|
||||
});
|
||||
|
||||
await bridge.requestOAuthUrlFromPanel({
|
||||
panelMode: 'sub2api',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiGroupName: 'codex',
|
||||
sub2apiAccountPriority: 3,
|
||||
}, { logLabel: '步骤 7' });
|
||||
try {
|
||||
const result = await bridge.requestOAuthUrlFromPanel({
|
||||
panelMode: 'sub2api',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiGroupName: 'codex',
|
||||
sub2apiDefaultProxyName: 'shadowrocket',
|
||||
}, { logLabel: '步骤 7' });
|
||||
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.equal(sentMessages[0].sourceName, 'sub2api-panel');
|
||||
assert.equal(sentMessages[0].message.payload.sub2apiAccountPriority, 3);
|
||||
const generateCall = fetchCalls.find((call) => call.path === '/api/v1/admin/openai/generate-auth-url');
|
||||
assert.equal(tabCreates.length, 0);
|
||||
assert.equal(sentMessages.length, 0);
|
||||
assert.equal(generateCall.body.proxy_id, 7);
|
||||
assert.deepStrictEqual(result, {
|
||||
oauthUrl: 'https://auth.openai.com/authorize?state=oauth-state',
|
||||
sub2apiSessionId: 'session-123',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
sub2apiGroupId: 5,
|
||||
sub2apiGroupIds: [5],
|
||||
sub2apiDraftName: result.sub2apiDraftName,
|
||||
sub2apiProxyId: 7,
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -80,72 +80,14 @@ test('platform verify module supports codex2api protocol callback exchange', asy
|
||||
}
|
||||
});
|
||||
|
||||
test('platform verify retries transient SUB2API oauth/token exchange errors before failing', async () => {
|
||||
test('platform verify retries transient SUB2API oauth/token exchange errors before succeeding', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
|
||||
const logs = [];
|
||||
const attempts = [];
|
||||
let callCount = 0;
|
||||
const executor = api.createStep10Executor({
|
||||
addLog: async (message, level = 'info') => {
|
||||
logs.push({ message, level });
|
||||
},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
closeConflictingTabsForSource: async () => {},
|
||||
completeStepFromBackground: async () => {},
|
||||
ensureContentScriptReadyOnTab: async () => {},
|
||||
getPanelMode: () => 'sub2api',
|
||||
getTabId: async () => 12,
|
||||
isLocalhostOAuthCallbackUrl: (value) => String(value || '').includes('/auth/callback?code='),
|
||||
isTabAlive: async () => true,
|
||||
normalizeCodex2ApiUrl: (value) => value,
|
||||
normalizeSub2ApiUrl: () => 'https://sub2api.example.com/admin/accounts',
|
||||
rememberSourceLastUrl: async () => {},
|
||||
reuseOrCreateTab: async () => 12,
|
||||
sendToContentScript: async (_source, message) => {
|
||||
attempts.push(message.type);
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
return {
|
||||
error: 'request failed: Post "https://auth.openai.com/oauth/token": unexpected EOF',
|
||||
};
|
||||
}
|
||||
return { ok: true };
|
||||
},
|
||||
sendToContentScriptResilient: async () => ({}),
|
||||
shouldBypassStep9ForLocalCpa: () => false,
|
||||
SUB2API_STEP9_RESPONSE_TIMEOUT_MS: 120000,
|
||||
});
|
||||
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub2api.example.com/admin/accounts',
|
||||
sub2apiEmail: 'flow@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiSessionId: 'session-123',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
});
|
||||
|
||||
assert.equal(callCount, 2);
|
||||
assert.deepStrictEqual(attempts, ['EXECUTE_STEP', 'EXECUTE_STEP']);
|
||||
assert.equal(
|
||||
logs.some((entry) => /临时网络波动/.test(entry.message) && entry.level === 'warn'),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test('platform verify retries transient SUB2API token_exchange_user_error before succeeding', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
|
||||
const logs = [];
|
||||
let callCount = 0;
|
||||
let contentScriptCalled = false;
|
||||
const executor = api.createStep10Executor({
|
||||
addLog: async (message, level = 'info') => {
|
||||
logs.push({ message, level });
|
||||
@@ -167,14 +109,22 @@ test('platform verify retries transient SUB2API token_exchange_user_error before
|
||||
rememberSourceLastUrl: async () => {},
|
||||
reuseOrCreateTab: async () => 12,
|
||||
sendToContentScript: async () => {
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
return {
|
||||
error: 'token exchange failed: status 400, body: { "error": { "message": "Invalid request. Please try again later.", "type": "invalid_request_error", "param": null, "code": "token_exchange_user_error" } }',
|
||||
};
|
||||
}
|
||||
return { ok: true };
|
||||
contentScriptCalled = true;
|
||||
return {};
|
||||
},
|
||||
createSub2ApiApi: () => ({
|
||||
submitOpenAiCallback: async () => {
|
||||
attempts.push('direct-api');
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
throw new Error('request failed: Post "https://auth.openai.com/oauth/token": unexpected EOF');
|
||||
}
|
||||
return {
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
verifiedStatus: 'SUB2API 已创建账号 #11',
|
||||
};
|
||||
},
|
||||
}),
|
||||
sendToContentScriptResilient: async () => ({}),
|
||||
shouldBypassStep9ForLocalCpa: () => false,
|
||||
SUB2API_STEP9_RESPONSE_TIMEOUT_MS: 120000,
|
||||
@@ -191,6 +141,74 @@ test('platform verify retries transient SUB2API token_exchange_user_error before
|
||||
});
|
||||
|
||||
assert.equal(callCount, 2);
|
||||
assert.equal(contentScriptCalled, false);
|
||||
assert.deepStrictEqual(attempts, ['direct-api', 'direct-api']);
|
||||
assert.equal(
|
||||
logs.some((entry) => /临时网络波动/.test(entry.message) && entry.level === 'warn'),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test('platform verify retries transient SUB2API token_exchange_user_error before succeeding', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
|
||||
const logs = [];
|
||||
let callCount = 0;
|
||||
let contentScriptCalled = false;
|
||||
const executor = api.createStep10Executor({
|
||||
addLog: async (message, level = 'info') => {
|
||||
logs.push({ message, level });
|
||||
},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
closeConflictingTabsForSource: async () => {},
|
||||
completeStepFromBackground: async () => {},
|
||||
ensureContentScriptReadyOnTab: async () => {},
|
||||
getPanelMode: () => 'sub2api',
|
||||
getTabId: async () => 12,
|
||||
isLocalhostOAuthCallbackUrl: (value) => String(value || '').includes('/auth/callback?code='),
|
||||
isTabAlive: async () => true,
|
||||
normalizeCodex2ApiUrl: (value) => value,
|
||||
normalizeSub2ApiUrl: () => 'https://sub2api.example.com/admin/accounts',
|
||||
rememberSourceLastUrl: async () => {},
|
||||
reuseOrCreateTab: async () => 12,
|
||||
sendToContentScript: async () => {
|
||||
contentScriptCalled = true;
|
||||
return { ok: true };
|
||||
},
|
||||
createSub2ApiApi: () => ({
|
||||
submitOpenAiCallback: async () => {
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
throw new Error('token exchange failed: status 400, body: { "error": { "message": "Invalid request. Please try again later.", "type": "invalid_request_error", "param": null, "code": "token_exchange_user_error" } }');
|
||||
}
|
||||
return {
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
verifiedStatus: 'SUB2API 已创建账号 #11',
|
||||
};
|
||||
},
|
||||
}),
|
||||
sendToContentScriptResilient: async () => ({}),
|
||||
shouldBypassStep9ForLocalCpa: () => false,
|
||||
SUB2API_STEP9_RESPONSE_TIMEOUT_MS: 120000,
|
||||
});
|
||||
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub2api.example.com/admin/accounts',
|
||||
sub2apiEmail: 'flow@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiSessionId: 'session-123',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
});
|
||||
|
||||
assert.equal(callCount, 2);
|
||||
assert.equal(contentScriptCalled, false);
|
||||
assert.equal(
|
||||
logs.some((entry) => /临时网络波动/.test(entry.message) && entry.level === 'warn'),
|
||||
true
|
||||
|
||||
@@ -42,6 +42,20 @@ function createDeps(overrides = {}) {
|
||||
return { deps, logs, completed, uiCalls };
|
||||
}
|
||||
|
||||
function loadStep10WithSub2Api() {
|
||||
const sub2apiSource = fs.readFileSync('background/sub2api-api.js', 'utf8');
|
||||
const step10Source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
return new Function('self', `${sub2apiSource}\n${step10Source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
}
|
||||
|
||||
function createSub2ApiResponse(payload, status = 200) {
|
||||
return {
|
||||
ok: status >= 200 && status < 300,
|
||||
status,
|
||||
text: async () => JSON.stringify(payload),
|
||||
};
|
||||
}
|
||||
|
||||
test('platform verify module submits CPA callback via management API first', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const originalFetch = globalThis.fetch;
|
||||
@@ -240,11 +254,109 @@ test('platform verify module rejects callback when cpa oauth state mismatches',
|
||||
}
|
||||
});
|
||||
|
||||
test('platform verify module sends Plus visible step 13 to SUB2API panel', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
test('platform verify module submits Plus visible step 13 to SUB2API via direct API', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const fetchCalls = [];
|
||||
const sentMessages = [];
|
||||
globalThis.fetch = async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
const body = options.body ? JSON.parse(options.body) : null;
|
||||
fetchCalls.push({ path: parsed.pathname, method: options.method || 'GET', body });
|
||||
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
if (parsed.pathname === '/api/v1/auth/login') {
|
||||
return createSub2ApiResponse({ code: 0, data: { access_token: 'admin-token' } });
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/groups/all') {
|
||||
return createSub2ApiResponse({ code: 0, data: [{ id: 5, name: 'codex', platform: 'openai' }] });
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/openai/exchange-code') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: {
|
||||
access_token: 'openai-access',
|
||||
refresh_token: 'openai-refresh',
|
||||
email: 'flow@example.com',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/accounts') {
|
||||
return createSub2ApiResponse({ code: 0, data: { id: 11 } });
|
||||
}
|
||||
return createSub2ApiResponse({ code: 1, message: `unexpected path ${parsed.pathname}` }, 404);
|
||||
};
|
||||
|
||||
const api = loadStep10WithSub2Api();
|
||||
const { deps, completed } = createDeps({
|
||||
getPanelMode: () => 'sub2api',
|
||||
getTabId: async () => 91,
|
||||
isTabAlive: async () => true,
|
||||
sendToContentScript: async (sourceName, message, options) => {
|
||||
sentMessages.push({ sourceName, message, options });
|
||||
return { ok: true };
|
||||
},
|
||||
});
|
||||
const executor = api.createStep10Executor(deps);
|
||||
|
||||
try {
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
visibleStep: 13,
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiGroupName: 'codex',
|
||||
sub2apiSessionId: 'session-1',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
});
|
||||
|
||||
const exchangeCall = fetchCalls.find((call) => call.path === '/api/v1/admin/openai/exchange-code');
|
||||
const createCall = fetchCalls.find((call) => call.path === '/api/v1/admin/accounts');
|
||||
assert.equal(sentMessages.length, 0);
|
||||
assert.equal(exchangeCall.body.code, 'callback-code');
|
||||
assert.equal(exchangeCall.body.state, 'oauth-state');
|
||||
assert.deepStrictEqual(createCall.body.group_ids, [5]);
|
||||
assert.deepStrictEqual(completed, [{
|
||||
step: 13,
|
||||
payload: {
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
verifiedStatus: 'SUB2API 已创建账号 #11',
|
||||
},
|
||||
}]);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('platform verify module forwards SUB2API account priority to direct create API', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const fetchCalls = [];
|
||||
const sentMessages = [];
|
||||
globalThis.fetch = async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
const body = options.body ? JSON.parse(options.body) : null;
|
||||
fetchCalls.push({ path: parsed.pathname, method: options.method || 'GET', body });
|
||||
|
||||
if (parsed.pathname === '/api/v1/auth/login') {
|
||||
return createSub2ApiResponse({ code: 0, data: { access_token: 'admin-token' } });
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/openai/exchange-code') {
|
||||
return createSub2ApiResponse({
|
||||
code: 0,
|
||||
data: {
|
||||
access_token: 'openai-access',
|
||||
refresh_token: 'openai-refresh',
|
||||
email: 'flow@example.com',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/api/v1/admin/accounts') {
|
||||
return createSub2ApiResponse({ code: 0, data: { id: 11 } });
|
||||
}
|
||||
return createSub2ApiResponse({ code: 1, message: `unexpected path ${parsed.pathname}` }, 404);
|
||||
};
|
||||
|
||||
const api = loadStep10WithSub2Api();
|
||||
const { deps } = createDeps({
|
||||
getPanelMode: () => 'sub2api',
|
||||
getTabId: async () => 91,
|
||||
@@ -256,50 +368,23 @@ test('platform verify module sends Plus visible step 13 to SUB2API panel', async
|
||||
});
|
||||
const executor = api.createStep10Executor(deps);
|
||||
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
visibleStep: 13,
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiSessionId: 'session-1',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
});
|
||||
try {
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiSessionId: 'session-1',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
sub2apiGroupId: 5,
|
||||
sub2apiAccountPriority: 2,
|
||||
});
|
||||
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.equal(sentMessages[0].sourceName, 'sub2api-panel');
|
||||
assert.equal(sentMessages[0].message.step, 13);
|
||||
});
|
||||
|
||||
test('platform verify module forwards SUB2API account priority to panel', async () => {
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const sentMessages = [];
|
||||
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
const { deps } = createDeps({
|
||||
getPanelMode: () => 'sub2api',
|
||||
getTabId: async () => 91,
|
||||
isTabAlive: async () => true,
|
||||
sendToContentScript: async (sourceName, message, options) => {
|
||||
sentMessages.push({ sourceName, message, options });
|
||||
return { ok: true };
|
||||
},
|
||||
});
|
||||
const executor = api.createStep10Executor(deps);
|
||||
|
||||
await executor.executeStep10({
|
||||
panelMode: 'sub2api',
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||
sub2apiEmail: 'admin@example.com',
|
||||
sub2apiPassword: 'secret',
|
||||
sub2apiSessionId: 'session-1',
|
||||
sub2apiOAuthState: 'oauth-state',
|
||||
sub2apiAccountPriority: 2,
|
||||
});
|
||||
|
||||
assert.equal(sentMessages.length, 1);
|
||||
assert.equal(sentMessages[0].sourceName, 'sub2api-panel');
|
||||
assert.equal(sentMessages[0].message.payload.sub2apiAccountPriority, 2);
|
||||
const createCall = fetchCalls.find((call) => call.path === '/api/v1/admin/accounts');
|
||||
assert.equal(sentMessages.length, 0);
|
||||
assert.equal(createCall.body.priority, 2);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user