149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'bun:test';
|
|
import {
|
|
parseCargoPackageVersion,
|
|
setCargoLockPackageVersion,
|
|
setCargoPackageVersion,
|
|
validateAppVersion,
|
|
} from '../scripts/version.mjs';
|
|
import { applyAppVersion, runCargoMetadata } from '../scripts/set-version.mjs';
|
|
|
|
describe('app version', () => {
|
|
it('reads the version from the package section', () => {
|
|
expect(parseCargoPackageVersion(`
|
|
[package]
|
|
name = "cpa-gui"
|
|
version = "1.2.3-beta.1+build.7"
|
|
|
|
[dependencies]
|
|
example = "9.9.9"
|
|
`)).toBe('1.2.3-beta.1+build.7');
|
|
});
|
|
|
|
it('rejects a missing package version', () => {
|
|
expect(() => parseCargoPackageVersion('[package]\nname = "cpa-gui"\n'))
|
|
.toThrow('Missing package version');
|
|
});
|
|
|
|
it('rejects an invalid semantic version', () => {
|
|
expect(() => parseCargoPackageVersion('[package]\nversion = "v1.2.3"\n'))
|
|
.toThrow('Invalid package version');
|
|
});
|
|
|
|
it('updates only the package version', () => {
|
|
const manifest = `[package]
|
|
name = "cpa-gui"
|
|
version = "1.2.3"
|
|
|
|
[dependencies]
|
|
example = "9.9.9"
|
|
`;
|
|
const updated = setCargoPackageVersion(manifest, '2.0.0-rc.1');
|
|
expect(parseCargoPackageVersion(updated)).toBe('2.0.0-rc.1');
|
|
expect(updated).toContain('example = "9.9.9"');
|
|
});
|
|
|
|
it('validates a version supplied by a release tag', () => {
|
|
expect(validateAppVersion('0.2.15')).toBe('0.2.15');
|
|
expect(() => validateAppVersion('v0.2.15')).toThrow('Invalid app version');
|
|
});
|
|
|
|
it('updates only the local app entry in Cargo.lock', () => {
|
|
const lockfile = `[[package]]
|
|
name = "cpa-gui"
|
|
version = "1.2.3"
|
|
|
|
[[package]]
|
|
name = "dependency"
|
|
version = "1.2.3"
|
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
`;
|
|
const updated = setCargoLockPackageVersion(lockfile, 'cpa-gui', '2.0.0');
|
|
expect(updated).toContain('name = "cpa-gui"\nversion = "2.0.0"');
|
|
expect(updated).toContain('name = "dependency"\nversion = "1.2.3"');
|
|
});
|
|
|
|
it('repairs a stale lockfile even when Cargo.toml already has the requested version', async () => {
|
|
const files = new Map<string, string>([
|
|
['Cargo.toml', '[package]\nname = "cpa-gui"\nversion = "2.0.0"\n'],
|
|
['Cargo.lock', '[[package]]\nname = "cpa-gui"\nversion = "1.0.0"\n'],
|
|
]);
|
|
const writes: string[] = [];
|
|
|
|
const result = await applyAppVersion('2.0.0', {
|
|
rootDir: 'test-root',
|
|
readText: async (path: string) => files.get(
|
|
path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock',
|
|
)!,
|
|
writeText: async (path: string, contents: string) => {
|
|
const name = path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock';
|
|
writes.push(name);
|
|
files.set(name, contents);
|
|
},
|
|
validateProject: async () => {},
|
|
});
|
|
|
|
expect(result).toMatchObject({ version: '2.0.0', previousVersion: '2.0.0', changed: true });
|
|
expect(writes).toEqual(['Cargo.lock']);
|
|
expect(files.get('Cargo.lock')).toContain('version = "2.0.0"');
|
|
});
|
|
|
|
it('restores both files when cargo metadata validation fails', async () => {
|
|
const originalManifest = '[package]\nname = "cpa-gui"\nversion = "1.0.0"\n';
|
|
const originalLockfile = '[[package]]\nname = "cpa-gui"\nversion = "1.0.0"\n';
|
|
const files = new Map<string, string>([
|
|
['Cargo.toml', originalManifest],
|
|
['Cargo.lock', originalLockfile],
|
|
]);
|
|
|
|
await expect(applyAppVersion('2.0.0', {
|
|
rootDir: 'test-root',
|
|
readText: async (path: string) => files.get(
|
|
path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock',
|
|
)!,
|
|
writeText: async (path: string, contents: string) => {
|
|
files.set(path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock', contents);
|
|
},
|
|
validateProject: async () => { throw new Error('metadata validation failed'); },
|
|
})).rejects.toThrow('metadata validation failed');
|
|
|
|
expect(files.get('Cargo.toml')).toBe(originalManifest);
|
|
expect(files.get('Cargo.lock')).toBe(originalLockfile);
|
|
});
|
|
|
|
it('restores the manifest when writing the lockfile fails', async () => {
|
|
const originalManifest = '[package]\nname = "cpa-gui"\nversion = "1.0.0"\n';
|
|
const originalLockfile = '[[package]]\nname = "cpa-gui"\nversion = "1.0.0"\n';
|
|
const files = new Map<string, string>([
|
|
['Cargo.toml', originalManifest],
|
|
['Cargo.lock', originalLockfile],
|
|
]);
|
|
let lockfileWrites = 0;
|
|
|
|
await expect(applyAppVersion('2.0.0', {
|
|
rootDir: 'test-root',
|
|
readText: async (path: string) => files.get(
|
|
path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock',
|
|
)!,
|
|
writeText: async (path: string, contents: string) => {
|
|
const name = path.endsWith('Cargo.toml') ? 'Cargo.toml' : 'Cargo.lock';
|
|
if (name === 'Cargo.lock' && lockfileWrites++ === 0) {
|
|
throw new Error('lockfile write failed');
|
|
}
|
|
files.set(name, contents);
|
|
},
|
|
validateProject: async () => {},
|
|
})).rejects.toThrow('lockfile write failed');
|
|
|
|
expect(files.get('Cargo.toml')).toBe(originalManifest);
|
|
expect(files.get('Cargo.lock')).toBe(originalLockfile);
|
|
});
|
|
|
|
it('includes cargo stderr when metadata validation fails', () => {
|
|
expect(() => runCargoMetadata('Cargo.toml', () => ({
|
|
status: 101,
|
|
stderr: 'manifest parse error',
|
|
stdout: '',
|
|
}))).toThrow('cargo metadata failed with exit code 101: manifest parse error');
|
|
});
|
|
});
|