From d15b2583bc081fb95ae545c2f890a10ce7937006 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Wed, 9 Sep 2026 14:37:47 +0800 Subject: [PATCH] test(suite): cover boundary and multi-target rollback Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- cli/test/unit/services/suite-service.test.ts | 32 +++++++++++++++++++ .../SkillSuiteCompositionPolicyTest.java | 19 +++++++++++ .../suite/SkillSuiteDraftServiceTest.java | 1 + 3 files changed, 52 insertions(+) diff --git a/cli/test/unit/services/suite-service.test.ts b/cli/test/unit/services/suite-service.test.ts index 7fcab0ff..e2892d55 100644 --- a/cli/test/unit/services/suite-service.test.ts +++ b/cli/test/unit/services/suite-service.test.ts @@ -487,6 +487,38 @@ describe('Suite local lifecycle', () => { expect(await exists(join(home, '.skillhub', 'inventory.json'))).toBe(false) }) + test('rolls back the first Agent target when the second target commit fails', async () => { + const home = await mkdtemp(join(tmpdir(), 'skillhub-suite-home-')) + const codexRoot = await mkdtemp(join(tmpdir(), 'skillhub-suite-codex-')) + const claudeRoot = await mkdtemp(join(tmpdir(), 'skillhub-suite-claude-')) + const { plan, downloads } = makePlan() + const renameOperation: typeof rename = async (source, target): Promise => { + if (String(source).includes('.skillhub-suite-stage-') + && String(target) === join(claudeRoot, 'alpha')) { + throw new Error('injected second target failure') + } + await rename(source, target) + } + + await expect(installSuite({ + registry, + namespace: 'global', + slug: 'starter-pack', + targets: [ + { agent: 'codex', rootDir: codexRoot, scope: 'project', source: 'explicit' }, + { agent: 'claude', rootDir: claudeRoot, scope: 'project', source: 'explicit' } + ], + force: false, + home, + client: clientFor(plan, downloads), + renameOperation + })).rejects.toThrow('injected second target failure') + + expect(await readdir(codexRoot)).toEqual([]) + expect(await readdir(claudeRoot)).toEqual([]) + expect(await exists(join(home, '.skillhub', 'inventory.json'))).toBe(false) + }) + test('reports retained backup paths when rollback cannot restore a replaced member', async () => { const home = await mkdtemp(join(tmpdir(), 'skillhub-suite-home-')) const rootDir = await mkdtemp(join(tmpdir(), 'skillhub-suite-root-')) diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteCompositionPolicyTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteCompositionPolicyTest.java index c72b243a..e9f82466 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteCompositionPolicyTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteCompositionPolicyTest.java @@ -6,10 +6,29 @@ import org.junit.jupiter.api.Test; import java.util.List; import java.util.stream.LongStream; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; class SkillSuiteCompositionPolicyTest { + @Test + void acceptsOneMemberWhenThatMemberIsTheEntry() { + List members = List.of(member(10L, 101L, "1.0.0")); + + assertThatCode(() -> SkillSuiteCompositionPolicy.validate(members, 101L)) + .doesNotThrowAnyException(); + } + + @Test + void acceptsExactlyOneHundredMembersWithOneEntry() { + List members = LongStream.rangeClosed(1, 100) + .mapToObj(id -> member(id, id + 1000, "1.0.0")) + .toList(); + + assertThatCode(() -> SkillSuiteCompositionPolicy.validate(members, 1001L)) + .doesNotThrowAnyException(); + } + @Test void rejectsSuiteWithoutEntrySkill() { List members = List.of(member(10L, 101L, "1.0.0")); diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteDraftServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteDraftServiceTest.java index 0ba27b51..7f6bd6de 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteDraftServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/suite/SkillSuiteDraftServiceTest.java @@ -73,6 +73,7 @@ class SkillSuiteDraftServiceTest { assertThat(saved.getSkillVersionId()).isEqualTo(40L); assertThat(saved.getPosition()).isZero(); assertThat(saved.getFingerprintSnapshot()).isEqualTo("sha256:abc"); + assertThat(saved.isEntry()).isTrue(); }); assertThat(result.version().getOverview()).isEqualTo("## Start here"); verify(publicationValidator).validate(result.suite(), result.version());