1
0
Fork 0
mirror of https://github.com/imjasonh/apidiff-action synced 2026-07-06 22:52:43 +00:00

Merge branch 'main' into codex/find-and-fix-a-bug

This commit is contained in:
Jason Hall 2025-08-08 18:27:11 -04:00 committed by GitHub
commit 7a5335dd10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 4135 deletions

4063
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,30 +7,16 @@
},
"description": "GitHub Action for detecting breaking API changes in Go code",
"devDependencies": {
"@types/jest": "^30.0.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^9.32.0",
"eslint-config-prettier": "^10.1.8",
"husky": "^9.1.7",
"jest": "^30.0.5",
"lint-staged": "^16.1.4",
"prettier": "^3.1.1"
},
"engines": {
"node": ">=20.0.0 <21.0.0"
},
"jest": {
"collectCoverage": true,
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
},
"testEnvironment": "node"
},
"keywords": [
"github-action",
"go",
@ -56,7 +42,7 @@
"format": "prettier --write .",
"lint": "eslint .",
"prepare": "husky install",
"test": "jest"
"test": "node --test"
},
"version": "1.0.0"
}

View file

@ -1,12 +1,14 @@
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const { parseApidiffOutput, formatChangesAsMarkdown } = require('../lib/parser');
describe('parseApidiffOutput', () => {
test('parses empty output', () => {
const result = parseApidiffOutput('');
expect(result.hasBreakingChanges).toBe(false);
expect(result.breakingCount).toBe(0);
expect(result.compatibleCount).toBe(0);
expect(result.packages).toEqual([]);
assert.strictEqual(result.hasBreakingChanges, false);
assert.strictEqual(result.breakingCount, 0);
assert.strictEqual(result.compatibleCount, 0);
assert.deepStrictEqual(result.packages, []);
});
test('parses output with no changes', () => {
@ -14,69 +16,55 @@ describe('parseApidiffOutput', () => {
const output = '';
const result = parseApidiffOutput(output);
expect(result.hasBreakingChanges).toBe(false);
expect(result.breakingCount).toBe(0);
expect(result.compatibleCount).toBe(0);
expect(result.packages).toEqual([]);
assert.strictEqual(result.hasBreakingChanges, false);
assert.strictEqual(result.breakingCount, 0);
assert.strictEqual(result.compatibleCount, 0);
assert.deepStrictEqual(result.packages, []);
});
test('parses output with breaking changes only', () => {
const output = `Incompatible changes:
- (*Client).DoSomething: changed from func(string) error to func(context.Context, string) error
- MaxRetries: removed`;
const output = `Incompatible changes:\n- (*Client).DoSomething: changed from func(string) error to func(context.Context, string) error\n- MaxRetries: removed`;
const result = parseApidiffOutput(output);
expect(result.hasBreakingChanges).toBe(true);
expect(result.breakingCount).toBe(2);
expect(result.compatibleCount).toBe(0);
expect(result.packages).toHaveLength(1);
expect(result.packages[0].name).toBe('default');
expect(result.packages[0].breaking).toHaveLength(2);
expect(result.packages[0].compatible).toHaveLength(0);
assert.strictEqual(result.hasBreakingChanges, true);
assert.strictEqual(result.breakingCount, 2);
assert.strictEqual(result.compatibleCount, 0);
assert.strictEqual(result.packages.length, 1);
assert.strictEqual(result.packages[0].name, 'default');
assert.strictEqual(result.packages[0].breaking.length, 2);
assert.strictEqual(result.packages[0].compatible.length, 0);
});
test('parses output with compatible changes only', () => {
const output = `Compatible changes:
- NewOption: added
- WithTimeout: added
- DefaultTimeout: added`;
const output = `Compatible changes:\n- NewOption: added\n- WithTimeout: added\n- DefaultTimeout: added`;
const result = parseApidiffOutput(output);
expect(result.hasBreakingChanges).toBe(false);
expect(result.breakingCount).toBe(0);
expect(result.compatibleCount).toBe(3);
expect(result.packages).toHaveLength(1);
expect(result.packages[0].compatible).toHaveLength(3);
assert.strictEqual(result.hasBreakingChanges, false);
assert.strictEqual(result.breakingCount, 0);
assert.strictEqual(result.compatibleCount, 3);
assert.strictEqual(result.packages.length, 1);
assert.strictEqual(result.packages[0].compatible.length, 3);
});
test('parses output with both breaking and compatible changes', () => {
const output = `Incompatible changes:
- (*Client).DoSomething: changed from func(string) error to func(context.Context, string) error
Compatible changes:
- NewOption: added
- WithTimeout: added`;
const output = `Incompatible changes:\n- (*Client).DoSomething: changed from func(string) error to func(context.Context, string) error\nCompatible changes:\n- NewOption: added\n- WithTimeout: added`;
const result = parseApidiffOutput(output);
expect(result.hasBreakingChanges).toBe(true);
expect(result.breakingCount).toBe(1);
expect(result.compatibleCount).toBe(2);
expect(result.packages).toHaveLength(1);
expect(result.packages[0].breaking).toHaveLength(1);
expect(result.packages[0].compatible).toHaveLength(2);
assert.strictEqual(result.hasBreakingChanges, true);
assert.strictEqual(result.breakingCount, 1);
assert.strictEqual(result.compatibleCount, 2);
assert.strictEqual(result.packages.length, 1);
assert.strictEqual(result.packages[0].breaking.length, 1);
assert.strictEqual(result.packages[0].compatible.length, 2);
});
test('parses real apidiff output', () => {
const output = `Incompatible changes:
- (*Greeter).Greet: changed from func() string to func(bool) string
- MaxRetries: removed
- Process: changed from func(string) (string, error) to func(context.Context, string) (string, error)
Compatible changes:
- Greeter.Language: added`;
const output = `Incompatible changes:\n- (*Greeter).Greet: changed from func() string to func(bool) string\n- MaxRetries: removed\n- Process: changed from func(string) (string, error) to func(context.Context, string) (string, error)\nCompatible changes:\n- Greeter.Language: added`;
const result = parseApidiffOutput(output);
expect(result.hasBreakingChanges).toBe(true);
expect(result.breakingCount).toBe(3);
expect(result.compatibleCount).toBe(1);
assert.strictEqual(result.hasBreakingChanges, true);
assert.strictEqual(result.breakingCount, 3);
assert.strictEqual(result.compatibleCount, 1);
});
});
@ -90,7 +78,7 @@ describe('formatChangesAsMarkdown', () => {
};
const markdown = formatChangesAsMarkdown(parsedChanges);
expect(markdown).toContain('No API changes detected');
assert.ok(markdown.includes('No API changes detected'));
});
test('formats breaking changes', () => {
@ -108,11 +96,11 @@ describe('formatChangesAsMarkdown', () => {
};
const markdown = formatChangesAsMarkdown(parsedChanges);
expect(markdown).toContain('Breaking changes | 1');
expect(markdown).toContain('⚠️ **This PR contains breaking API changes!**');
expect(markdown).toContain('❌ Breaking changes');
expect(markdown).toContain('- Foo: removed');
expect(markdown).not.toContain('`default`'); // Should not show default package name
assert.ok(markdown.includes('Breaking changes | 1'));
assert.ok(markdown.includes('⚠️ **This PR contains breaking API changes!**'));
assert.ok(markdown.includes('❌ Breaking changes'));
assert.ok(markdown.includes('- Foo: removed'));
assert.ok(!markdown.includes('`default`'));
});
test('formats compatible changes', () => {
@ -133,9 +121,9 @@ describe('formatChangesAsMarkdown', () => {
};
const markdown = formatChangesAsMarkdown(parsedChanges);
expect(markdown).toContain('Compatible changes | 2');
expect(markdown).toContain('✅ Compatible changes');
expect(markdown).toContain('- Bar: added');
expect(markdown).toContain('- Baz: added');
assert.ok(markdown.includes('Compatible changes | 2'));
assert.ok(markdown.includes('✅ Compatible changes'));
assert.ok(markdown.includes('- Bar: added'));
assert.ok(markdown.includes('- Baz: added'));
});
});