Files
renovate/lib/modules/manager/homebrew/update.spec.ts
renovate[bot] 196101a6bb chore(deps): update dependency oxlint-tsgolint to v0.25.0 (main) (#44424)
* chore(deps): update dependency oxlint-tsgolint to v0.25.0

| datasource | package         | from   | to     |
| ---------- | --------------- | ------ | ------ |
| npm        | oxlint-tsgolint | 0.23.0 | 0.25.0 |

* chore: fix new no-unnecessary-type-assertion findings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Sebastian Poxhofer <sebastian.poxhofer@stedi.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 07:04:21 +00:00

666 lines
20 KiB
TypeScript

import { Readable } from 'node:stream';
import { codeBlock } from 'common-tags';
import { Fixtures } from '~test/fixtures.ts';
import * as httpMock from '~test/http-mock.ts';
import * as handlers from './handlers/index.ts';
import { updateDependency } from './index.ts';
const aide = Fixtures.get('aide.rb.sample');
const ibazel = Fixtures.get('ibazel.rb.sample');
const baseUrl = 'https://github.com';
describe('modules/manager/homebrew/update', () => {
it('updates "releases" github dependency', async () => {
const upgrade = {
currentValue: 'v0.16.1',
depName: 'Aide',
managerData: {
type: 'github' as const,
ownerName: 'aide',
repoName: 'aide',
sha256:
'0f2b7cecc70c1a27d35c06c98804fcdb9f326630de5d035afc447122186010b7',
url: 'https://github.com/aide/aide/releases/download/v0.16.1/aide-0.16.1.tar.gz',
urlType: 'releases' as const,
},
newValue: 'v0.17.7',
};
httpMock
.scope(baseUrl)
.get('/aide/aide/releases/download/v0.17.7/aide-0.17.7.tar.gz')
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: aide,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).not.toBe(aide);
expect(newContent).toContain(
'https://github.com/aide/aide/releases/download/v0.17.7/aide-0.17.7.tar.gz',
);
expect(newContent).toContain(
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
);
});
it('updates "archive" github dependency', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).not.toBe(ibazel);
expect(newContent).toContain(
'https://github.com/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
);
expect(newContent).toContain(
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
);
});
it('updates "archive" github dependency from old url format', async () => {
const oldArchiveFormat = codeBlock`
class Ibazel < Formula
desc 'IBazel is a tool for building Bazel targets when source files change.'
homepage 'https://github.com/bazelbuild/bazel-watcher'
url "https://github.com/bazelbuild/bazel-watcher/archive/v0.8.2.tar.gz"
sha256 '26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4'
end
`;
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: oldArchiveFormat,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).not.toBe(oldArchiveFormat);
expect(newContent).toContain(
'https://github.com/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
);
expect(newContent).toContain(
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
);
});
it('returns unchanged content if fromStream promise rejects', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.replyWithError('')
.get('/bazelbuild/bazel-watcher/archive/refs/tags/v0.9.3.tar.gz')
.replyWithError('');
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if url field in upgrade object is invalid', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'invalid_url',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if repoName in upgrade object is invalid', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'invalid/repo/name',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if repoName in upgrade object is wrong', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'wrong-version/archive/refs/tags/v10.2.3.tar.gz',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if url field in Formula file is invalid', async () => {
const invalidUrlFormula = codeBlock`
class Ibazel < Formula
desc 'IBazel is a tool for building Bazel targets when source files change.'
homepage 'https://github.com/bazelbuild/bazel-watcher'
url ???https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz"
sha256 '26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4'
end
`;
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: invalidUrlFormula,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(invalidUrlFormula);
});
it('returns unchanged content if url field in Formula file is missing', async () => {
const missingUrlFormula = codeBlock`
class Ibazel < Formula
desc 'IBazel is a tool for building Bazel targets when source files change.'
homepage 'https://github.com/bazelbuild/bazel-watcher'
sha256 '26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4'
end
`;
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: missingUrlFormula,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(missingUrlFormula);
});
it('returns unchanged content if sha256 field in Formula file is invalid', async () => {
const invalidSha256Formula = codeBlock`
class Ibazel < Formula
desc 'IBazel is a tool for building Bazel targets when source files change.'
homepage 'https://github.com/bazelbuild/bazel-watcher'
url "https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz"
sha256 ???26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4'
end
`;
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: invalidSha256Formula,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(invalidSha256Formula);
});
it('returns unchanged content if sha256 field in Formula file is missing', async () => {
const missingSha256Formula = codeBlock`
class Ibazel < Formula
desc 'IBazel is a tool for building Bazel targets when source files change.'
homepage 'https://github.com/bazelbuild/bazel-watcher'
url "https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz"
end
`;
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
urlType: 'archive' as const,
},
newValue: 'v0.9.3',
};
httpMock
.scope(baseUrl)
.get(
'/bazelbuild/bazel-watcher/releases/download/v0.9.3/bazel-watcher-0.9.3.tar.gz',
)
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: missingSha256Formula,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(missingSha256Formula);
});
it('returns unchanged content if both got requests fail', async () => {
const upgrade = {
currentValue: 'v0.16.1',
depName: 'Aide',
managerData: {
type: 'github' as const,
ownerName: 'aide',
repoName: 'aide',
sha256:
'0f2b7cecc70c1a27d35c06c98804fcdb9f326630de5d035afc447122186010b7',
url: 'https://github.com/aide/aide/releases/download/v0.16.1/aide-0.16.1.tar.gz',
urlType: 'releases' as const,
},
newValue: 'v0.17.7',
};
httpMock
.scope(baseUrl)
.get('/aide/aide/releases/download/v0.17.7/aide-0.17.7.tar.gz')
.replyWithError('')
.get('/aide/aide/archive/refs/tags/v0.17.7.tar.gz')
.replyWithError('');
const newContent = await updateDependency({
fileContent: aide,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(aide);
});
it('returns unchanged content if managerData is missing required fields', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256: null,
url: null,
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content for unknown handler type', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'unknown' as never,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if newValue is missing', async () => {
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
},
newValue: undefined as never,
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
});
it('returns unchanged content if handler buildArchiveUrls returns null', async () => {
const mockHandler = {
type: 'github',
parseUrl: vi.fn().mockReturnValue({
type: 'github',
currentValue: 'v0.8.2',
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
urlType: 'archive',
}),
buildArchiveUrls: vi.fn().mockReturnValue(null),
createDependency: vi.fn(),
};
vi.spyOn(handlers, 'findHandlerByType').mockReturnValue(mockHandler);
const upgrade = {
currentValue: 'v0.8.2',
depName: 'Ibazel',
managerData: {
type: 'github' as const,
ownerName: 'bazelbuild',
repoName: 'bazel-watcher',
sha256:
'26f5125218fad2741d3caf937b02296d803900e5f153f5b1f733f15391b9f9b4',
url: 'https://github.com/bazelbuild/bazel-watcher/archive/refs/tags/v0.8.2.tar.gz',
},
newValue: 'v0.9.3',
};
const newContent = await updateDependency({
fileContent: ibazel,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(ibazel);
expect(mockHandler.buildArchiveUrls).toHaveBeenCalled();
});
it('updates npm scoped package dependency', async () => {
const content = codeBlock`
class ClaudeCode < Formula
desc "Anthropic's official CLI for Claude"
homepage "https://www.anthropic.com/claude-code"
url "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.1.0.tgz"
sha256 "345eae3fe4c682df3d8876141f32035bb2898263ce5a406e76e1d74ccb13f601"
license "Proprietary"
end
`;
const upgrade = {
currentValue: '0.1.0',
depName: '@anthropic-ai/claude-code',
managerData: {
type: 'npm' as const,
packageName: '@anthropic-ai/claude-code',
sha256:
'345eae3fe4c682df3d8876141f32035bb2898263ce5a406e76e1d74ccb13f601',
url: 'https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.1.0.tgz',
},
newValue: '0.2.0',
};
httpMock
.scope('https://registry.npmjs.org')
.get('/@anthropic-ai/claude-code/-/claude-code-0.2.0.tgz')
.reply(200, Readable.from(['foo']));
const newContent = await updateDependency({
fileContent: content,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).not.toBe(content);
expect(newContent).toContain(
'https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.0.tgz',
);
expect(newContent).toContain(
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
);
});
it('updates npm unscoped package dependency', async () => {
const content = codeBlock`
class Express < Formula
desc "Fast, unopinionated, minimalist web framework"
homepage "https://expressjs.com/"
url "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
sha256 "abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234"
license "MIT"
end
`;
const upgrade = {
currentValue: '4.18.2',
depName: 'express',
managerData: {
type: 'npm' as const,
packageName: 'express',
sha256:
'abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234',
url: 'https://registry.npmjs.org/express/-/express-4.18.2.tgz',
},
newValue: '4.18.3',
};
httpMock
.scope('https://registry.npmjs.org')
.get('/express/-/express-4.18.3.tgz')
.reply(200, Readable.from(['bar']));
const newContent = await updateDependency({
fileContent: content,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).not.toBe(content);
expect(newContent).toContain(
'https://registry.npmjs.org/express/-/express-4.18.3.tgz',
);
expect(newContent).toContain(
'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9',
);
});
it('returns unchanged content if npm tarball download fails', async () => {
const content = codeBlock`
class Express < Formula
desc "Fast, unopinionated, minimalist web framework"
homepage "https://expressjs.com/"
url "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
sha256 "abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234"
license "MIT"
end
`;
const upgrade = {
currentValue: '4.18.2',
depName: 'express',
managerData: {
type: 'npm' as const,
packageName: 'express',
sha256:
'abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234',
url: 'https://registry.npmjs.org/express/-/express-4.18.2.tgz',
},
newValue: '4.18.3',
};
httpMock
.scope('https://registry.npmjs.org')
.get('/express/-/express-4.18.3.tgz')
.replyWithError('Not found');
const newContent = await updateDependency({
fileContent: content,
packageFile: 'Formula/some.rb',
upgrade,
});
expect(newContent).toBe(content);
});
});