发布入口接入灰度开关
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- 后端:GET /api/runtime/frontend-config 新增 gameDistributionPublishEnabled,复用 `game-distribution:publish` 判据(未配置或 enabled=false 时对已登录作者默认开放,显式收紧后只放行白名单/灰度命中用户,匿名恒为 false),前端入口与写入口共用同一事实源。 - 后端用例:新增 frontend_runtime_config_game_distribution_publish_is_scoped_to_authenticated_gate,覆盖默认开放、enabled=true 无白名单、白名单命中、deny 名单、enabled=false 回退与 rolloutPercent=100。 - 网页:平台壳按灰度隐藏「发布游戏 / 发布新版本」入口;/games/publish 直接访问时渲染「发布功能正在灰度中」并提供重新检查;读取失败按放行处理,由后端写入口把关并返回可读文案。 - AGC:新增 readGamePublishAvailability(同一运行时配置字段),只有命中才把发布回调交给 DirectProject 聊天头;字段缺失或读取失败按不开放处理。 - 文档:玩法链路、后端数据契约与实施计划记录灰度口径、入口行为与验证命令。
This commit is contained in:
@@ -58,6 +58,15 @@ const gameDistributionMock = vi.hoisted(() => ({
|
||||
|
||||
vi.mock('../../services/gameDistributionClient', () => gameDistributionMock);
|
||||
|
||||
const frontendRuntimeConfigMock = vi.hoisted(() => ({
|
||||
loadFrontendRuntimeConfig: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/frontendRuntimeConfigService', () => ({
|
||||
loadFrontendRuntimeConfig: (...args: unknown[]) =>
|
||||
frontendRuntimeConfigMock.loadFrontendRuntimeConfig(...args),
|
||||
}));
|
||||
|
||||
vi.mock('../auth/AuthUiContext', () => ({
|
||||
useAuthUi: () => authUiMock.value,
|
||||
}));
|
||||
@@ -206,6 +215,12 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
for (const mock of Object.values(gameDistributionMock)) {
|
||||
mock.mockReset();
|
||||
}
|
||||
frontendRuntimeConfigMock.loadFrontendRuntimeConfig.mockReset();
|
||||
frontendRuntimeConfigMock.loadFrontendRuntimeConfig.mockResolvedValue({
|
||||
imageEditorAgentSidebarEnabled: false,
|
||||
agcTemplateLibraryEnabled: false,
|
||||
gameDistributionPublishEnabled: true,
|
||||
});
|
||||
authUiMock.value.openLoginModal.mockReset();
|
||||
responsiveMock.isDesktopLayout = true;
|
||||
});
|
||||
@@ -850,3 +865,47 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('游戏发布灰度入口', () => {
|
||||
function loginAsAuthor() {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
displayName: '测试作者',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: null,
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
};
|
||||
authUiMock.value.canAccessProtectedData = true;
|
||||
}
|
||||
|
||||
it('灰度未命中时广场不展示发布入口', async () => {
|
||||
loginAsAuthor();
|
||||
frontendRuntimeConfigMock.loadFrontendRuntimeConfig.mockResolvedValue({
|
||||
imageEditorAgentSidebarEnabled: false,
|
||||
agcTemplateLibraryEnabled: false,
|
||||
gameDistributionPublishEnabled: false,
|
||||
});
|
||||
gameDistributionMock.listGames.mockResolvedValue([]);
|
||||
|
||||
render(<StatefulPlatformEntryFlowShell initialStage="games" />);
|
||||
|
||||
expect(await screen.findByText('游戏广场')).toBeTruthy();
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByRole('button', { name: '发布游戏' })).toBeNull(),
|
||||
);
|
||||
});
|
||||
|
||||
it('灰度命中时广场展示发布入口', async () => {
|
||||
loginAsAuthor();
|
||||
gameDistributionMock.listGames.mockResolvedValue([]);
|
||||
|
||||
render(<StatefulPlatformEntryFlowShell initialStage="games" />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('button', { name: '发布游戏' }),
|
||||
).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user