Skip to content

Commit 0e0fb59

Browse files
committed
chore: round of test approval
1 parent a01f0ca commit 0e0fb59

11 files changed

Lines changed: 249 additions & 398 deletions

File tree

packages/core/tests/util/deepMerge.test.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,11 @@ describe("deepMerge", () => {
4545
expect(deepMerge(target, source)).toEqual({ a: { b: { c: 10, d: 2, f: 4 }, e: 3, g: 5 } });
4646
});
4747

48-
/**
49-
* SECURITY: prototype pollution via constructor key.
50-
*
51-
* The current implementation uses spread (`{ ...target }`) and `Object.keys()`,
52-
* which treats `constructor` as a regular own property on the result object
53-
* rather than walking up the prototype chain. This means `Object.prototype`
54-
* is NOT polluted by this particular vector.
55-
*
56-
* However, the implementation does NOT explicitly reject dangerous keys
57-
* (`__proto__`, `constructor`, `prototype`). If the merge strategy changes
58-
* (e.g., to mutate in place), pollution could be re-introduced. Consider
59-
* adding an explicit key blocklist for defense in depth.
60-
*/
61-
test("SECURITY: prototype pollution via constructor key does not pollute Object.prototype", () => {
48+
test("prototype pollution via constructor key does not pollute Object.prototype", () => {
6249
const malicious = JSON.parse('{"constructor":{"prototype":{"polluted":true}}}');
63-
6450
const result = deepMerge({}, malicious);
6551

66-
// Object.prototype must NOT be polluted
6752
expect(({} as Record<string, unknown>).polluted).toBeUndefined();
68-
69-
// The constructor key should exist as a plain property on the result
7053
expect(result).toHaveProperty("constructor");
7154
});
7255

packages/core/tests/util/mri-utils.test.ts

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,29 @@ import { buildMriOptions, convertNumbers, mapPositionals, validatePositionals }
66
describe("buildMriOptions", () => {
77
test("boolean args go to boolean array", () => {
88
const defs: Record<string, ArgDefinition> = { debug: { type: "boolean" }, verbose: { type: "boolean" } };
9-
const opts = buildMriOptions(defs);
10-
expect(opts.boolean).toEqual(["debug", "verbose"]);
11-
expect(opts.string).toEqual([]);
9+
10+
expect(buildMriOptions(defs)).toMatchObject({ boolean: ["debug", "verbose"], string: [] });
1211
});
1312

1413
test("string and number args go to string array", () => {
1514
const defs: Record<string, ArgDefinition> = { count: { type: "number" }, name: { type: "string" } };
16-
const opts = buildMriOptions(defs);
17-
expect(opts.string).toEqual(["count", "name"]);
18-
expect(opts.boolean).toEqual([]);
15+
16+
expect(buildMriOptions(defs)).toMatchObject({ boolean: [], string: ["count", "name"] });
1917
});
2018

2119
test("aliases registered correctly", () => {
2220
const defs: Record<string, ArgDefinition> = {
2321
output: { alias: "o", type: "string" },
2422
verbose: { alias: "v", type: "boolean" },
2523
};
26-
const opts = buildMriOptions(defs);
27-
expect(opts.alias.v).toBe("verbose");
28-
expect(opts.alias.o).toBe("output");
24+
25+
expect(buildMriOptions(defs).alias).toMatchObject({ o: "output", v: "verbose" });
2926
});
3027

3128
test("camelCase auto-generates kebab-case alias", () => {
3229
const defs: Record<string, ArgDefinition> = { dryRun: { type: "boolean" }, outputDir: { type: "string" } };
33-
const opts = buildMriOptions(defs);
34-
expect(opts.alias["dry-run"]).toBe("dryRun");
35-
expect(opts.alias["output-dir"]).toBe("outputDir");
30+
31+
expect(buildMriOptions(defs).alias).toMatchObject({ "dry-run": "dryRun", "output-dir": "outputDir" });
3632
});
3733

3834
test("defaults populated", () => {
@@ -60,9 +56,8 @@ describe("convertNumbers", () => {
6056

6157
test("skips boolean and string types", () => {
6258
const defs: Record<string, ArgDefinition> = { name: { type: "string" }, verbose: { type: "boolean" } };
63-
const result = convertNumbers({ name: "hello", verbose: true }, defs);
64-
expect(result.verbose).toBe(true);
65-
expect(result.name).toBe("hello");
59+
60+
expect(convertNumbers({ name: "hello", verbose: true }, defs)).toMatchObject({ name: "hello", verbose: true });
6661
});
6762

6863
test("skips undefined values", () => {
@@ -81,30 +76,27 @@ describe("mapPositionals", () => {
8176

8277
test("maps multiple positionals by index", () => {
8378
const defs: Record<string, PositionalDefinition> = { dest: { required: true }, source: { required: true } };
84-
const result = mapPositionals(["a.txt", "b.txt"], defs);
85-
expect(result.dest).toBe("a.txt");
86-
expect(result.source).toBe("b.txt");
79+
80+
expect(mapPositionals(["a.txt", "b.txt"], defs)).toMatchObject({ dest: "a.txt", source: "b.txt" });
8781
});
8882

8983
test("variadic positional captures rest as array", () => {
9084
const defs: Record<string, PositionalDefinition> = { first: { required: true }, rest: { variadic: true } };
91-
const result = mapPositionals(["a", "b", "c", "d"], defs);
92-
expect(result.first).toBe("a");
93-
expect(result.rest).toEqual(["b", "c", "d"]);
85+
86+
expect(mapPositionals(["a", "b", "c", "d"], defs)).toMatchObject({ first: "a", rest: ["b", "c", "d"] });
9487
});
9588

9689
test("missing positional returns undefined", () => {
9790
const defs: Record<string, PositionalDefinition> = { extra: {}, name: { required: true } };
98-
const result = mapPositionals(["foo"], defs);
99-
expect(result.extra).toBe("foo");
100-
expect(result.name).toBeUndefined();
91+
92+
expect(mapPositionals(["foo"], defs)).toMatchObject({ extra: "foo", name: undefined });
10193
});
10294

10395
test("extra positionals silently dropped", () => {
10496
const defs: Record<string, PositionalDefinition> = { name: { required: true } };
10597
const result = mapPositionals(["foo", "bar", "baz"], defs);
106-
expect(result.name).toBe("foo");
107-
expect(Object.keys(result)).toEqual(["name"]);
98+
99+
expect(result).toEqual({ name: "foo" });
108100
});
109101
});
110102

packages/sisyphus/src/domain/Commit.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ export class Commit {
130130
const commits: Commit[] = [];
131131

132132
for (let i = 0; i < segments.length; i += 2) {
133-
const fields = segments[i]?.split(FIELD_SEPARATOR);
133+
const segment = segments[i];
134+
if (!segment) continue;
135+
const fields = segment.split(FIELD_SEPARATOR);
134136
if (fields.length < 3) continue;
135137

136138
const [hash, subject, author] = fields;

packages/sisyphus/tests/domain/BumpType.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ describe("BUMP_ORDER", () => {
7373
BumpType.Dependency,
7474
BumpType.Snapshot,
7575
]);
76-
expect(BUMP_ORDER).toHaveLength(5);
7776
});
7877
});
7978

@@ -88,11 +87,9 @@ describe("BUMP_PRIORITY", () => {
8887
});
8988

9089
describe("BUMP_EMOJI", () => {
91-
test("all types have an emoji", () => {
90+
test("all types have a non-empty emoji string", () => {
9291
for (const type of Object.values(BumpType)) {
93-
expect(BUMP_EMOJI[type]).toBeDefined();
94-
expect(typeof BUMP_EMOJI[type]).toBe("string");
95-
expect(BUMP_EMOJI[type].length).toBeGreaterThan(0);
92+
expect(BUMP_EMOJI[type]).toMatch(/.+/);
9693
}
9794
});
9895
});
Lines changed: 58 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,137 @@
11
import { describe, expect, test } from "bun:test";
2+
import { OTHER_COMMIT_TYPE } from "../../src/constants";
23
import type { CommitInfo } from "../../src/domain/Commit";
3-
import { Commit, OTHER_COMMIT_TYPE } from "../../src/domain/Commit";
4+
import { Commit } from "../../src/domain/Commit";
45

56
const HASH = "abc1234567890def";
7+
const AUTHOR = "test-author";
68

79
describe("Commit.parse", () => {
810
test("parses standard feat commit", () => {
9-
const commit = Commit.parse(HASH, "feat: add feature");
10-
11-
expect(commit.type).toBe("feat");
12-
expect(commit.message).toBe("add feature");
13-
expect(commit.breaking).toBe(false);
14-
expect(commit.scope).toBeUndefined();
15-
expect(commit.subject).toBe("feat: add feature");
16-
expect(commit.hash).toBe(HASH);
17-
expect(commit.files).toEqual([]);
11+
const commit = Commit.parse(HASH, "feat: add feature", AUTHOR);
12+
13+
expect(commit).toMatchObject({
14+
breaking: false,
15+
files: [],
16+
hash: HASH,
17+
message: "add feature",
18+
scope: undefined,
19+
subject: "feat: add feature",
20+
type: "feat",
21+
});
1822
});
1923

2024
test("parses standard fix commit", () => {
21-
const commit = Commit.parse(HASH, "fix: resolve bug");
25+
const commit = Commit.parse(HASH, "fix: resolve bug", AUTHOR);
2226

23-
expect(commit.type).toBe("fix");
24-
expect(commit.message).toBe("resolve bug");
25-
expect(commit.breaking).toBe(false);
27+
expect(commit).toMatchObject({ breaking: false, message: "resolve bug", type: "fix" });
2628
});
2729

2830
test("parses commit with scope", () => {
29-
const commit = Commit.parse(HASH, "feat(api): add endpoint");
31+
const commit = Commit.parse(HASH, "feat(api): add endpoint", AUTHOR);
3032

31-
expect(commit.type).toBe("feat");
32-
expect(commit.scope).toBe("api");
33-
expect(commit.message).toBe("add endpoint");
34-
expect(commit.breaking).toBe(false);
33+
expect(commit).toMatchObject({ breaking: false, message: "add endpoint", scope: "api", type: "feat" });
3534
});
3635

3736
test("parses breaking change with !", () => {
38-
const commit = Commit.parse(HASH, "feat!: breaking change");
37+
const commit = Commit.parse(HASH, "feat!: breaking change", AUTHOR);
3938

40-
expect(commit.type).toBe("feat");
41-
expect(commit.breaking).toBe(true);
42-
expect(commit.message).toBe("breaking change");
39+
expect(commit).toMatchObject({ breaking: true, message: "breaking change", type: "feat" });
4340
});
4441

4542
test("parses breaking change with scope and !", () => {
46-
const commit = Commit.parse(HASH, "feat(api)!: break it");
43+
const commit = Commit.parse(HASH, "feat(api)!: break it", AUTHOR);
4744

48-
expect(commit.type).toBe("feat");
49-
expect(commit.scope).toBe("api");
50-
expect(commit.breaking).toBe(true);
51-
expect(commit.message).toBe("break it");
45+
expect(commit).toMatchObject({ breaking: true, message: "break it", scope: "api", type: "feat" });
5246
});
5347

5448
describe("all known commit types", () => {
5549
const knownTypes = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "style", "test"];
5650

5751
for (const type of knownTypes) {
5852
test(`recognizes "${type}" as conventional type`, () => {
59-
const commit = Commit.parse(HASH, `${type}: some message`);
53+
const commit = Commit.parse(HASH, `${type}: some message`, AUTHOR);
6054

61-
expect(commit.type).toBe(type);
62-
expect(commit.message).toBe("some message");
63-
expect(commit.isConventional).toBe(true);
55+
expect(commit).toMatchObject({ isConventional: true, message: "some message", type });
6456
});
6557
}
6658
});
6759

6860
test("unknown type falls back to OTHER_COMMIT_TYPE", () => {
69-
const commit = Commit.parse(HASH, "unknown: something");
61+
const commit = Commit.parse(HASH, "unknown: something", AUTHOR);
7062

71-
expect(commit.type).toBe(OTHER_COMMIT_TYPE);
72-
expect(commit.type).toBe("other");
73-
expect(commit.message).toBe("unknown: something");
63+
expect(commit).toMatchObject({ message: "unknown: something", type: OTHER_COMMIT_TYPE });
7464
});
7565

7666
test("non-conventional commit falls back to OTHER_COMMIT_TYPE", () => {
77-
const commit = Commit.parse(HASH, "just a message");
67+
const commit = Commit.parse(HASH, "just a message", AUTHOR);
7868

79-
expect(commit.type).toBe(OTHER_COMMIT_TYPE);
80-
expect(commit.message).toBe("just a message");
81-
expect(commit.subject).toBe("just a message");
69+
expect(commit).toMatchObject({ message: "just a message", subject: "just a message", type: OTHER_COMMIT_TYPE });
8270
});
8371

8472
test("commit with colon but no known type falls back to OTHER_COMMIT_TYPE", () => {
85-
const commit = Commit.parse(HASH, "WIP: work in progress");
73+
const commit = Commit.parse(HASH, "WIP: work in progress", AUTHOR);
8674

87-
expect(commit.type).toBe(OTHER_COMMIT_TYPE);
88-
expect(commit.message).toBe("WIP: work in progress");
75+
expect(commit).toMatchObject({ message: "WIP: work in progress", type: OTHER_COMMIT_TYPE });
8976
});
9077

9178
test("empty subject edge case", () => {
92-
const commit = Commit.parse(HASH, "");
79+
const commit = Commit.parse(HASH, "", AUTHOR);
9380

94-
expect(commit.type).toBe(OTHER_COMMIT_TYPE);
95-
expect(commit.message).toBe("");
96-
expect(commit.subject).toBe("");
97-
expect(commit.breaking).toBe(false);
81+
expect(commit).toMatchObject({ breaking: false, message: "", subject: "", type: OTHER_COMMIT_TYPE });
9882
});
9983
});
10084

10185
describe("Commit getters", () => {
10286
test("shortHash returns first 7 characters", () => {
103-
const commit = Commit.parse(HASH, "feat: something");
104-
105-
expect(commit.shortHash).toBe("abc1234");
106-
expect(commit.shortHash).toHaveLength(7);
87+
expect(Commit.parse(HASH, "feat: something", AUTHOR).shortHash).toBe("abc1234");
10788
});
10889

10990
test("shortHash works with exactly 7 char hash", () => {
110-
const commit = Commit.parse("abc1234", "feat: something");
111-
112-
expect(commit.shortHash).toBe("abc1234");
91+
expect(Commit.parse("abc1234", "feat: something", AUTHOR).shortHash).toBe("abc1234");
11392
});
11493

11594
test("isConventional returns true for known types", () => {
116-
const commit = Commit.parse(HASH, "feat: something");
117-
118-
expect(commit.isConventional).toBe(true);
95+
expect(Commit.parse(HASH, "feat: something", AUTHOR).isConventional).toBe(true);
11996
});
12097

12198
test("isConventional returns false for other type", () => {
122-
const commit = Commit.parse(HASH, "random message");
123-
124-
expect(commit.isConventional).toBe(false);
99+
expect(Commit.parse(HASH, "random message", AUTHOR).isConventional).toBe(false);
125100
});
126101
});
127102

128103
describe("Commit.withFiles", () => {
129-
test("returns new Commit with files set", () => {
130-
const original = Commit.parse(HASH, "feat: something");
104+
test("returns new Commit with files set, original unchanged", () => {
105+
const original = Commit.parse(HASH, "feat: something", AUTHOR);
131106
const files = ["src/index.ts", "src/utils.ts"];
132107
const withFiles = original.withFiles(files);
133108

134-
expect(withFiles.files).toEqual(files);
135-
expect(withFiles.type).toBe("feat");
136-
expect(withFiles.message).toBe("something");
137-
expect(withFiles.hash).toBe(HASH);
138-
// Original is unchanged
109+
expect(withFiles).toMatchObject({ files, hash: HASH, message: "something", type: "feat" });
139110
expect(original.files).toEqual([]);
140111
});
141112
});
142113

143114
describe("Commit.withBody", () => {
144-
test("returns new Commit with body set", () => {
145-
const original = Commit.parse(HASH, "feat: something");
115+
test("returns new Commit with body set, original unchanged", () => {
116+
const original = Commit.parse(HASH, "feat: something", AUTHOR);
146117
const withBody = original.withBody("detailed description");
147118

148-
expect(withBody.body).toBe("detailed description");
149-
expect(withBody.type).toBe("feat");
150-
expect(withBody.message).toBe("something");
151-
// Original is unchanged
119+
expect(withBody).toMatchObject({ body: "detailed description", message: "something", type: "feat" });
152120
expect(original.body).toBeUndefined();
153121
});
154122

155-
test("returns new Commit with undefined body", () => {
156-
const original = Commit.parse(HASH, "feat: something").withBody("some body");
157-
const withoutBody = original.withBody(undefined);
123+
test("withBody(undefined) clears the body", () => {
124+
const original = Commit.parse(HASH, "feat: something", AUTHOR).withBody("some body");
158125

159-
expect(withoutBody.body).toBeUndefined();
126+
expect(original.withBody(undefined).body).toBeUndefined();
160127
});
161128
});
162129

163130
describe("Commit.toInfo", () => {
164131
test("returns CommitInfo with correct fields", () => {
165-
const commit = Commit.parse(HASH, "feat(api): add endpoint").withBody("some body").withFiles(["src/api.ts"]);
166-
132+
const commit = Commit.parse(HASH, "feat(api): add endpoint", AUTHOR)
133+
.withBody("some body")
134+
.withFiles(["src/api.ts"]);
167135
const info: CommitInfo = commit.toInfo(["@org/api"]);
168136

169137
expect(info).toEqual({
@@ -178,15 +146,16 @@ describe("Commit.toInfo", () => {
178146
});
179147

180148
test("returns CommitInfo for non-conventional commit", () => {
181-
const commit = Commit.parse(HASH, "just a message");
182-
183-
const info = commit.toInfo([]);
149+
const info = Commit.parse(HASH, "just a message", AUTHOR).toInfo([]);
184150

185-
expect(info.type).toBe("other");
186-
expect(info.message).toBe("just a message");
187-
expect(info.hash).toBe("abc1234");
188-
expect(info.packages).toEqual([]);
189-
expect(info.scope).toBeUndefined();
190-
expect(info.body).toBeUndefined();
151+
expect(info).toEqual({
152+
body: undefined,
153+
hash: "abc1234",
154+
message: "just a message",
155+
packages: [],
156+
scope: undefined,
157+
subject: "just a message",
158+
type: "other",
159+
});
191160
});
192161
});

0 commit comments

Comments
 (0)