import { codeBlock } from 'common-tags'; import { Fixtures } from '~test/fixtures.ts'; import { fs } from '~test/util.ts'; import * as hostRules from '../../../util/host-rules.ts'; import type { ExtractConfig } from '../types.ts'; import { extractPackageFile } from './index.ts'; vi.mock('../../../util/fs/index.ts'); function mockReadLocalFile(files: Record) { fs.readLocalFile.mockImplementation((file): Promise => { let content: string | null = null; if (file in files) { content = files[file]; } return Promise.resolve(content); }); } const cargo1toml = Fixtures.get('Cargo.1.toml'); const cargo2toml = Fixtures.get('Cargo.2.toml'); const cargo3toml = Fixtures.get('Cargo.3.toml'); const cargo4toml = Fixtures.get('Cargo.4.toml'); const cargo5toml = Fixtures.get('Cargo.5.toml'); const cargo6configtoml = Fixtures.get('cargo.6.config.toml'); const cargo6toml = Fixtures.get('Cargo.6.toml'); const cargo7toml = Fixtures.get('Cargo.7.toml'); const lockfileUpdateCargotoml = Fixtures.get('lockfile-update/Cargo.toml'); describe('modules/manager/cargo/extract', () => { describe('extractPackageFile()', () => { const config: ExtractConfig = {}; beforeEach(() => { vi.stubEnv('CARGO_REGISTRIES_PRIVATE_CRATES_INDEX', undefined); vi.stubEnv('CARGO_REGISTRIES_MCORBIN_INDEX', undefined); hostRules.clear(); hostRules.add({ hostType: 'github', matchHost: 'git.example.com', }); }); it('returns null for invalid toml', async () => { await expect( extractPackageFile('invalid toml', 'Cargo.toml', config), ).resolves.toBeNull(); }); it('returns null for empty dependencies', async () => { const cargotoml = '[dependencies]\n'; await expect( extractPackageFile(cargotoml, 'Cargo.toml', config), ).resolves.toBeNull(); }); it('returns null for empty dev-dependencies', async () => { const cargotoml = '[dev-dependencies]\n'; await expect( extractPackageFile(cargotoml, 'Cargo.toml', config), ).resolves.toBeNull(); }); it('returns null for empty custom target', async () => { const cargotoml = '[target."foo".dependencies]\n'; await expect( extractPackageFile(cargotoml, 'Cargo.toml', config), ).resolves.toBeNull(); }); it('extracts multiple dependencies simple', async () => { const res = await extractPackageFile(cargo1toml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'libc', currentValue: '=0.2.43' }, { depName: 'bitflags', currentValue: '1.0.4' }, { depName: 'pcap-sys', currentValue: '=0.1', skipReason: 'path-dependency', }, { depName: 'pnet', currentValue: '0.21.0' }, { depName: 'git_dep_with_version', datasource: 'git-refs', packageName: 'https://github.com/foo/bar', skipReason: 'unspecified-version', }, { depName: 'git_dep', datasource: 'git-refs', packageName: 'https://github.com/foo/bar', skipReason: 'unspecified-version', }, { depName: 'path_dep_without_version', skipReason: 'path-dependency' }, { depName: 'git_dep_with_tag', datasource: 'github-tags', currentValue: '1.10.3', packageName: 'foo/bar', registryUrls: ['https://github.com'], }, { depName: 'git_dep_with_rev', datasource: 'git-refs', currentDigest: 'abc1234', packageName: 'https://github.com/foo/bar', }, { depName: 'git_dep_with_branch', datasource: 'git-refs', currentValue: 'next', packageName: 'https://github.com/foo/bar', skipReason: 'git-dependency', }, { depName: 'git_dep_with_custom_hostname', datasource: 'github-tags', currentValue: '1.0.0', packageName: 'a/b', registryUrls: ['https://git.example.com'], }, { depName: 'same_version_1__', currentValue: '0.0.0' }, { depName: 'same_version_1_', currentValue: '0.0.0' }, { depName: 'same_version_1', currentValue: '0.0.0' }, { depName: 'dep1', currentValue: '0.4.2' }, { depName: 'winapi', currentValue: '=0.3.6' }, { depName: 'wasm-bindgen', currentValue: '0.2.37' }, { depName: 'js-sys', currentValue: '0.3.14' }, { depName: 'js_relative_import', skipReason: 'path-dependency' }, { depName: 'web-sys', currentValue: '0.3.14' }, ]); }); it('extracts multiple dependencies advanced', async () => { const res = await extractPackageFile(cargo2toml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'amethyst_animation', currentValue: '0.2.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_assets', currentValue: '0.3.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_audio', currentValue: '0.2.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_config', currentValue: '0.6.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_core', currentValue: '0.2.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_controls', currentValue: '0.1.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_locale', currentValue: '0.1.0', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_renderer', currentValue: '0.7', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_input', currentValue: '0.3', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_ui', currentValue: '0.2', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'amethyst_utils', currentValue: '0.2', depType: 'dependencies', skipReason: 'path-dependency', }, { depName: 'derivative', currentValue: '1.0', depType: 'dependencies' }, { depName: 'fern', currentValue: '0.5', depType: 'dependencies' }, { depName: 'log', currentValue: '0.4', depType: 'dependencies' }, { depName: 'rayon', currentValue: '1.0.1', depType: 'dependencies' }, { depName: 'rustc_version_runtime', currentValue: '0.1', depType: 'dependencies', }, { depName: 'winit', currentValue: '0.15', depType: 'dependencies' }, { depName: 'thread_profiler', currentValue: '0.1', depType: 'dependencies', }, { depName: 'amethyst_gltf', currentValue: '0.2', depType: 'dev-dependencies', skipReason: 'path-dependency', }, { depName: 'env_logger', currentValue: '0.5.10', depType: 'dev-dependencies', }, { depName: 'genmesh', currentValue: '0.6', depType: 'dev-dependencies', }, { depName: 'ron', currentValue: '0.2', depType: 'dev-dependencies' }, { depName: 'serde', currentValue: '1.0', depType: 'dev-dependencies' }, { depName: 'serde_derive', currentValue: '1.0', depType: 'dev-dependencies', }, { depName: 'vergen', currentValue: '0.1', depType: 'build-dependencies', }, ]); }); it('handles inline tables', async () => { const res = await extractPackageFile(cargo3toml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'pcap-sys', currentValue: '0.1', skipReason: 'path-dependency', }, { depName: 'pnet', currentValue: '0.21.0' }, { depName: 'dep1', currentValue: '1.2', skipReason: 'path-dependency' }, { depName: 'dep2', currentValue: '3.4', skipReason: 'path-dependency' }, { depName: 'dep3', currentValue: '~12.3.1', skipReason: 'path-dependency', }, { depName: 'dep4', currentValue: 'INVALID 3.3.1 VERSION' }, { depName: 'dep5', currentValue: '3.2.1' }, { depName: 'dep6', skipReason: 'invalid-dependency-specification' }, ]); }); it('handles standard tables', async () => { const res = await extractPackageFile(cargo4toml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'dep1', currentValue: '1.2', skipReason: 'path-dependency' }, { depName: 'dep2', currentValue: '3.4', skipReason: 'path-dependency' }, { depName: 'dep3', currentValue: '~12.3.1', skipReason: 'path-dependency', }, { depName: 'dep4', currentValue: 'INVALID 1.3.1 VERSION', skipReason: 'path-dependency', }, { depName: 'dep5', skipReason: 'path-dependency' }, { depName: 'dep7', skipReason: 'invalid-dependency-specification' }, ]); }); it('extracts platform specific dependencies', async () => { const res = await extractPackageFile(cargo5toml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'wasm-bindgen', currentValue: '0.2.37', target: 'cfg(target_arch = "wasm32")', }, { depName: 'js-sys', currentValue: '0.3.14', target: 'cfg(target_arch = "wasm32")', }, { depName: 'js_relative_import', target: 'cfg(target_arch = "wasm32")', skipReason: 'path-dependency', }, { depName: 'web-sys', currentValue: '0.3.14', target: 'cfg(target_arch = "wasm32")', }, ]); }); it('extracts registry urls from .cargo/config.toml', async () => { mockReadLocalFile({ '.cargo/config.toml': cargo6configtoml }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toMatchObject([ { depName: 'proprietary-crate', currentValue: '0.1.0', registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, { depName: 'mcorbin-test', currentValue: '3.0.0', registryUrls: ['https://github.com/mcorbin/testregistry'], }, { depName: 'tokio', currentValue: '0.2' }, ]); }); it('extracts registry urls from .cargo/config (legacy path)', async () => { mockReadLocalFile({ '.cargo/config.toml': cargo6configtoml }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toMatchObject([ { depName: 'proprietary-crate', currentValue: '0.1.0', registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, { depName: 'mcorbin-test', currentValue: '3.0.0', registryUrls: ['https://github.com/mcorbin/testregistry'], }, { depName: 'tokio', currentValue: '0.2' }, ]); }); it('extracts overridden registry indexes from .cargo/config.toml', async () => { mockReadLocalFile({ '.cargo/config.toml': codeBlock`[registries] private-crates = { index = "https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git" } [registries.mcorbin] index = "https://github.com/mcorbin/testregistry" [source.crates-io] replace-with = "mcorbin" [source.mcorbin] replace-with = "private-crates"`, }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.1.0', datasource: 'crate', depName: 'proprietary-crate', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'private-crates', }, registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, { currentValue: '3.0.0', datasource: 'crate', depName: 'mcorbin-test', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'mcorbin', }, registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, ]); }); it('extracts overridden source registry indexes from .cargo/config.toml', async () => { mockReadLocalFile({ '.cargo/config.toml': codeBlock`[source.crates-io-replacement] registry = "https://github.com/replacement/testregistry" [source.crates-io] replace-with = "crates-io-replacement"`, }); const res = await extractPackageFile(cargo7toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, registryUrls: ['https://github.com/replacement/testregistry'], }, ]); }); it('extracts registries overridden to the default', async () => { mockReadLocalFile({ '.cargo/config.toml': codeBlock`[source.mcorbin] replace-with = "crates-io" [source.private-crates] replace-with = "mcorbin"`, }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.1.0', datasource: 'crate', depName: 'proprietary-crate', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'private-crates', }, }, { currentValue: '3.0.0', datasource: 'crate', depName: 'mcorbin-test', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'mcorbin', }, }, { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, }, ]); }); it('extracts registries with an empty config.toml', async () => { mockReadLocalFile({ '.cargo/config.toml': '' }); const res = await extractPackageFile(cargo5toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.2.37', datasource: 'crate', depName: 'wasm-bindgen', depType: 'dependencies', managerData: { nestedVersion: false, }, target: 'cfg(target_arch = "wasm32")', }, { currentValue: '0.3.14', datasource: 'crate', depName: 'js-sys', depType: 'dependencies', managerData: { nestedVersion: false, }, target: 'cfg(target_arch = "wasm32")', }, { currentValue: '', datasource: 'crate', depName: 'js_relative_import', depType: 'dependencies', managerData: { nestedVersion: false, }, skipReason: 'path-dependency', target: 'cfg(target_arch = "wasm32")', }, { currentValue: '0.3.14', datasource: 'crate', depName: 'web-sys', depType: 'dependencies', managerData: { nestedVersion: true, }, target: 'cfg(target_arch = "wasm32")', }, ]); }); it('extracts registry urls from environment', async () => { vi.stubEnv( 'CARGO_REGISTRIES_PRIVATE_CRATES_INDEX', 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ); vi.stubEnv( 'CARGO_REGISTRIES_MCORBIN_INDEX', 'https://github.com/mcorbin/testregistry', ); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.1.0', datasource: 'crate', depName: 'proprietary-crate', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'private-crates', }, registryUrls: [ 'https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git', ], }, { currentValue: '3.0.0', datasource: 'crate', depName: 'mcorbin-test', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'mcorbin', }, registryUrls: ['https://github.com/mcorbin/testregistry'], }, { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, }, ]); }); it('extracts workspace dependencies', async () => { const cargoToml = codeBlock` [package] name = "renovate-test" version = "0.1.0" authors = ["John Doe "] edition = "2018" [dependencies] git2 = "0.14.0" [workspace] members = ["pcap-sys"] [workspace.dependencies] serde = "1.0.146" tokio = { version = "1.21.1" }`; const res = await extractPackageFile(cargoToml, 'Cargo.toml', config); expect(res?.deps).toEqual([ { currentValue: '0.14.0', datasource: 'crate', depName: 'git2', depType: 'dependencies', managerData: { nestedVersion: false }, }, { currentValue: '1.0.146', datasource: 'crate', depName: 'serde', depType: 'workspace.dependencies', managerData: { nestedVersion: false }, }, { currentValue: '1.21.1', datasource: 'crate', depName: 'tokio', depType: 'workspace.dependencies', managerData: { nestedVersion: true, }, }, ]); }); it('skips workspace dependency', async () => { const cargotoml = '[dependencies]\nfoobar = { workspace = true }'; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).toEqual([ { currentValue: '', datasource: 'crate', depName: 'foobar', depType: 'dependencies', managerData: { nestedVersion: false, }, skipReason: 'inherited-dependency', }, ]); }); it('skips unknown registries', async () => { const cargotoml = '[dependencies]\nfoobar = { version = "0.1.0", registry = "not-listed" }'; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'foobar', currentValue: '0.1.0', skipReason: 'unknown-registry', }, ]); }); it('fails to parse cargo config with invalid TOML', async () => { mockReadLocalFile({ '.cargo/config': '[registries' }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toMatchObject([ { depName: 'proprietary-crate', currentValue: '0.1.0', skipReason: 'unknown-registry', }, { depName: 'mcorbin-test', currentValue: '3.0.0', skipReason: 'unknown-registry', }, { depName: 'tokio', currentValue: '0.2' }, ]); }); it('ignore cargo config registries with missing index', async () => { mockReadLocalFile({ '.cargo/config': '[registries.mine]\nfoo = "bar"' }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toMatchObject([ { depName: 'proprietary-crate', currentValue: '0.1.0', skipReason: 'unknown-registry', }, { depName: 'mcorbin-test', currentValue: '3.0.0', skipReason: 'unknown-registry', }, { depName: 'tokio', currentValue: '0.2' }, ]); }); it('ignore cargo config source replaced registries with missing index', async () => { mockReadLocalFile({ '.cargo/config': codeBlock`[registries.mine] foo = "bar" [source.crates-io] replace-with = "mine"`, }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.1.0', datasource: 'crate', depName: 'proprietary-crate', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'private-crates', }, skipReason: 'unknown-registry', }, { currentValue: '3.0.0', datasource: 'crate', depName: 'mcorbin-test', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'mcorbin', }, skipReason: 'unknown-registry', }, { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, skipReason: 'unknown-registry', }, ]); }); it('ignore cargo config with circular registry source replacements', async () => { mockReadLocalFile({ '.cargo/config': codeBlock`[registries] private-crates = { index = "https://dl.cloudsmith.io/basic/my-org/my-repo/cargo/index.git" } [registries.mcorbin] index = "https://github.com/mcorbin/testregistry" [source.crates-io] replace-with = "mcorbin" [source.mcorbin] replace-with = "private-crates" [source.private-crates] replace-with = "mcorbin" `, }); const res = await extractPackageFile(cargo6toml, 'Cargo.toml', { ...config, }); expect(res?.deps).toEqual([ { currentValue: '0.1.0', datasource: 'crate', depName: 'proprietary-crate', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'private-crates', }, skipReason: 'unknown-registry', }, { currentValue: '3.0.0', datasource: 'crate', depName: 'mcorbin-test', depType: 'dependencies', managerData: { nestedVersion: true, registryName: 'mcorbin', }, skipReason: 'unknown-registry', }, { currentValue: '0.2', datasource: 'crate', depName: 'tokio', depType: 'dependencies', managerData: { nestedVersion: false, }, skipReason: 'unknown-registry', }, ]); }); it('extracts original package name of renamed dependencies', async () => { const cargotoml = '[dependencies]\nboolector-solver = { package = "boolector", version = "0.4.0" }'; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'boolector-solver', currentValue: '0.4.0', packageName: 'boolector', }, ]); }); it('keeps the git source as packageName for renamed git dependencies', async () => { const cargotoml = codeBlock` [dependencies] github-tag = { package = "real-crate", git = "https://github.com/foo/bar", tag = "v1.2.3" } gitlab-tag = { package = "real-crate", git = "https://gitlab.com/foo/bar", tag = "v1.2.3" } other-tag = { package = "real-crate", git = "https://gitea.example.com/foo/bar", tag = "v1.2.3" } rev = { package = "real-crate", git = "https://github.com/foo/bar", rev = "abcdef0" } crates-io = { package = "real-crate", version = "0.4.0" } `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([ { depName: 'github-tag', datasource: 'github-tags', packageName: 'foo/bar', registryUrls: ['https://github.com'], currentValue: 'v1.2.3', }, { depName: 'gitlab-tag', datasource: 'gitlab-tags', packageName: 'foo/bar', registryUrls: ['https://gitlab.com'], currentValue: 'v1.2.3', }, { depName: 'other-tag', datasource: 'git-tags', packageName: 'https://gitea.example.com/foo/bar', currentValue: 'v1.2.3', }, { depName: 'rev', datasource: 'git-refs', packageName: 'https://github.com/foo/bar', currentDigest: 'abcdef0', }, { depName: 'crates-io', datasource: 'crate', packageName: 'real-crate', currentValue: '0.4.0', }, ]); }); it('extracts locked versions', async () => { const cargolock = Fixtures.get('lockfile-update/Cargo.3.lock'); mockReadLocalFile({ 'Cargo.lock': cargolock }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const cargotoml = codeBlock` [package] name = "test" version = "0.1.0" edition = "2021" [dependencies] syn = "2.0" `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).toMatchObject([{ lockedVersion: '2.0.1' }]); }); it('does not extract locked versions for git dependencies', async () => { const cargolock = Fixtures.get('lockfile-update/Cargo.3.lock'); mockReadLocalFile({ 'Cargo.lock': cargolock }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const cargotoml = codeBlock` [package] name = "test" version = "0.1.0" edition = "2021" [dependencies] git_dep = { git = "https://github.com/foo/bar" } `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps[0].lockedVersion).toBeUndefined(); }); it('extracts locked versions for renamed packages', async () => { const cargolock = Fixtures.get('lockfile-update/Cargo.1.lock'); mockReadLocalFile({ 'Cargo.lock': cargolock }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const res = await extractPackageFile( lockfileUpdateCargotoml, 'Cargo.toml', config, ); expect(res?.deps).toMatchObject([ { lockedVersion: '2.0.1' }, { lockedVersion: '1.0.1' }, ]); }); it('handles missing locked versions', async () => { const cargolock = Fixtures.get('lockfile-update/Cargo.2.lock'); mockReadLocalFile({ 'Cargo.lock': cargolock }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const res = await extractPackageFile( lockfileUpdateCargotoml, 'Cargo.toml', config, ); expect(res?.deps).toMatchObject([ { lockedVersion: '2.0.1' }, expect.not.objectContaining({ lockedVersion: expect.anything() }), ]); }); it('handles invalid versions in the toml file', async () => { const cargolock = Fixtures.get('lockfile-update/Cargo.3.lock'); mockReadLocalFile({ 'Cargo.lock': cargolock }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const cargotoml = codeBlock` [package] name = "test" version = "0.1.0" edition = "2021" [dependencies] syn = "2.foo.1" `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.deps).not.toHaveProperty('lockedVersion'); }); it('handles invalid lock file', async () => { mockReadLocalFile({ 'Cargo.lock': 'foo' }); fs.findLocalSiblingOrParent.mockResolvedValueOnce('Cargo.lock'); const res = await extractPackageFile( lockfileUpdateCargotoml, 'Cargo.toml', config, ); expect(res?.deps).toMatchObject([ expect.not.objectContaining({ lockedVersion: expect.anything() }), expect.not.objectContaining({ lockedVersion: expect.anything() }), ]); }); it('should extract project version', async () => { const cargotoml = codeBlock` [package] name = "test" version = "0.1.0" edition = "2021" [dependencies] syn = "2.0" `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.packageFileVersion).toBe('0.1.0'); }); it('should extract project version from workspace', async () => { const cargotoml = codeBlock` [package] name = "test" version.workspace = true edition = "2021" [workspace.package] version = "0.1.0" [dependencies] syn = "2.0" `; const res = await extractPackageFile(cargotoml, 'Cargo.toml', config); expect(res?.packageFileVersion).toBe('0.1.0'); }); }); });