Skip to content

Commit

Permalink
Merge pull request #11585 from dependabot/add-specs-for-bun-ecosystem
Browse files Browse the repository at this point in the history
Add tests for the new bun ecosystem
  • Loading branch information
markhallen authored Feb 13, 2025
2 parents 17dc47a + 8419167 commit eca15c1
Show file tree
Hide file tree
Showing 42 changed files with 1,687 additions and 2 deletions.
4 changes: 2 additions & 2 deletions bun/lib/dependabot/bun/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def sentry_context
sig { override.returns(T::Array[Regexp]) }
def self.updated_files_regex
[
%r{^(?:.*/)?package\.json$},
%r{^(?:.*/)?\.pnp\.(?:js|cjs)$} # Matches .pnp.js or .pnp.cjs files
%r{^(?:.*\/)?package\.json$},
%r{^(?:.*\/)?bun\.lock$}
]
end

Expand Down
52 changes: 52 additions & 0 deletions bun/spec/dependabot/bun/bun_package_manager_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# typed: false
# frozen_string_literal: true

require "spec_helper"
require "dependabot/bun"

RSpec.describe Dependabot::Bun::BunPackageManager do
let(:package_manager) do
described_class.new(
detected_version: detected_version,
raw_version: raw_version
)
end

let(:detected_version) { "1" }
let(:raw_version) { "1.1.39" }

describe "#initialize" do
context "when version is a String" do
it "sets the version correctly" do
expect(package_manager.detected_version).to eq(Dependabot::Version.new(detected_version))
expect(package_manager.version).to eq(Dependabot::Version.new(raw_version))
end

it "sets the name correctly" do
expect(package_manager.name).to eq(described_class::NAME)
end

it "sets the deprecated_versions correctly" do
expect(package_manager.deprecated_versions).to eq(
described_class::DEPRECATED_VERSIONS
)
end

it "sets the supported_versions correctly" do
expect(package_manager.supported_versions).to eq(described_class::SUPPORTED_VERSIONS)
end
end
end

describe "#deprecated?" do
it "returns false" do
expect(package_manager.deprecated?).to be false
end
end

describe "#unsupported?" do
it "returns false" do
expect(package_manager.unsupported?).to be false
end
end
end
231 changes: 231 additions & 0 deletions bun/spec/dependabot/bun/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# typed: false
# frozen_string_literal: true

require "spec_helper"
require "dependabot/file_fetchers"
require "dependabot/bun"
require_common_spec "file_fetchers/shared_examples_for_file_fetchers"

RSpec.describe Dependabot::Bun::FileFetcher do
let(:json_header) { { "content-type" => "application/json" } }
let(:credentials) do
[Dependabot::Credential.new({
"type" => "git_source",
"host" => "github.com",
"username" => "x-access-token",
"password" => "token"
})]
end
let(:directory) { "/" }
let(:url) { "https://api.github.com/repos/gocardless/bump/contents/" }
let(:file_fetcher_instance) do
described_class.new(source: source, credentials: credentials)
end
let(:source) do
Dependabot::Source.new(
provider: "github",
repo: "gocardless/bump",
directory: directory
)
end

before do
allow(file_fetcher_instance).to receive(:commit).and_return("sha")

stub_request(:get, File.join(url, "?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: "[]",
headers: json_header
)

stub_request(:get, File.join(url, "package.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "package_json_content.json"),
headers: json_header
)
end

context "with a bun.lock but no package-lock.json file" do
before do
stub_request(:get, url + "?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "contents_js_bun.json"),
headers: json_header
)
stub_request(:get, File.join(url, "package-lock.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
stub_request(:get, File.join(url, "bun.lock?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "bun_lock_content.json"),
headers: json_header
)
end

describe "fetching and parsing the bun.lock" do
before do
allow(Dependabot::Experiments).to receive(:enabled?)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:enable_bun_ecosystem).and_return(enable_beta_ecosystems)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:enable_beta_ecosystems).and_return(enable_beta_ecosystems)
end

context "when the experiment :enable_beta_ecosystems is inactive" do
let(:enable_beta_ecosystems) { false }

it "does not fetch or parse the the bun.lock" do
expect(file_fetcher_instance.files.map(&:name))
.to match_array(%w(package.json))
expect(file_fetcher_instance.ecosystem_versions)
.to match({ package_managers: { "unknown" => an_instance_of(Integer) } })
end
end

context "when the experiment :enable_beta_ecosystems is active" do
let(:enable_beta_ecosystems) { true }

it "fetches and parses the bun.lock" do
expect(file_fetcher_instance.files.map(&:name))
.to match_array(%w(package.json bun.lock))
expect(file_fetcher_instance.ecosystem_versions)
.to match({ package_managers: { "bun" => an_instance_of(Integer) } })
end
end
end
end

context "with a path dependency" do
before do
stub_request(:get, File.join(url, "package.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "package_json_with_path_content.json"),
headers: json_header
)
end

context "with a bad package.json" do
before do
stub_request(:get, File.join(url, "package.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "gemfile_content.json"),
headers: json_header
)
end

it "raises a DependencyFileNotParseable error" do
expect { file_fetcher_instance.files }
.to raise_error(Dependabot::DependencyFileNotParseable) do |error|
expect(error.file_name).to eq("package.json")
end
end
end

context "with a bad dependencies object" do
before do
stub_request(:get, File.join(url, "package.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "package_json_with_dependency_arrays.json"),
headers: json_header
)
end

it "raises a DependencyFileNotParseable error" do
expect { file_fetcher_instance.files }
.to raise_error(Dependabot::DependencyFileNotParseable) do |error|
expect(error.file_name).to eq("package.json")
end
end
end

context "when path is fetchable" do
before do
stub_request(:get, File.join(url, "deps/etag/package.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "package_json_content.json"),
headers: json_header
)
end

it "fetches package.json from path dependency" do
expect(file_fetcher_instance.files.count).to eq(2)
expect(file_fetcher_instance.files.map(&:name))
.to include("deps/etag/package.json")
path_file = file_fetcher_instance.files
.find { |f| f.name == "deps/etag/package.json" }
expect(path_file.support_file?).to be(true)
end
end
end

context "with package.json file just including a dummy string" do
before do
allow(file_fetcher_instance).to receive(:commit).and_return("sha")
stub_request(:get, File.join(url, "package.json?ref=sha"))
.to_return(
status: 200,
body: fixture_to_response("projects/javascript/package_json_faked", "package.json"),
headers: json_header
)
end

it "raises a DependencyFileNotParseable error" do
expect { file_fetcher_instance.files }
.to raise_error(Dependabot::DependencyFileNotParseable) do |error|
expect(error.file_name).to eq("package.json")
end
end
end

context "with packageManager field not in x.y.z format" do
before do
allow(file_fetcher_instance).to receive(:commit).and_return("sha")
stub_request(:get, File.join(url, "package.json?ref=sha"))
.to_return(
status: 200,
body: fixture_to_response("projects/javascript/package_manager_unparseable", "package.json"),
headers: json_header
)
end

it "still fetches package.json fine" do
expect(file_fetcher_instance.files.count).to eq(1)
end
end

context "with lockfileVersion not in integer format" do
before do
allow(file_fetcher_instance).to receive(:commit).and_return("sha")
stub_request(:get, File.join(url, "package.json?ref=sha"))
.to_return(
status: 200,
body: fixture_to_response("projects/javascript/lockfile_version_unparseable", "package.json"),
headers: json_header
)
end

it "still fetches files" do
expect(file_fetcher_instance.files.count).to eq(1)
end
end
end

def fixture_to_response(dir, file)
JSON.dump({ "content" => Base64.encode64(fixture(dir, file)) })
end
95 changes: 95 additions & 0 deletions bun/spec/dependabot/bun/file_parser/lockfile_parser_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# typed: false
# frozen_string_literal: true

require "spec_helper"
require "dependabot/bun"

RSpec.describe Dependabot::Bun::FileParser::LockfileParser do
subject(:lockfile_parser) do
described_class.new(dependency_files: dependency_files)
end

describe "#parse" do
subject(:dependencies) { lockfile_parser.parse }

context "when dealing with bun.lock" do
context "when the lockfile is invalid" do
let(:dependency_files) { project_dependency_files("bun/invalid_lockfile") }

it "raises a DependencyFileNotParseable error" do
expect { dependencies }
.to raise_error(Dependabot::DependencyFileNotParseable) do |error|
expect(error.file_name).to eq("bun.lock")
expect(error.message).to eq("Invalid bun.lock file: malformed JSONC at line 3, column 1")
end
end
end

context "when the lockfile version is invalid" do
let(:dependency_files) { project_dependency_files("bun/invalid_lockfile_version") }

it "raises a DependencyFileNotParseable error" do
expect { dependencies }
.to raise_error(Dependabot::DependencyFileNotParseable) do |error|
expect(error.file_name).to eq("bun.lock")
expect(error.message).to include("lockfileVersion")
end
end
end

context "when dealing with v0 format" do
context "with a simple project" do
let(:dependency_files) { project_dependency_files("bun/simple_v0") }

it "parses dependencies properly" do
expect(dependencies.find { |d| d.name == "fetch-factory" }).to have_attributes(
name: "fetch-factory",
version: "0.0.1"
)
expect(dependencies.find { |d| d.name == "etag" }).to have_attributes(
name: "etag",
version: "1.8.1"
)
expect(dependencies.length).to eq(11)
end
end

context "with a simple workspace project" do
let(:dependency_files) { project_dependency_files("bun/simple_workspace_v0") }

it "parses dependencies properly" do
expect(dependencies.find { |d| d.name == "etag" }).to have_attributes(
name: "etag",
version: "1.8.1"
)
expect(dependencies.find { |d| d.name == "lodash" }).to have_attributes(
name: "lodash",
version: "1.3.1"
)
expect(dependencies.find { |d| d.name == "chalk" }).to have_attributes(
name: "chalk",
version: "0.3.0"
)
expect(dependencies.length).to eq(5)
end
end
end

context "when dealing with v1 format" do
let(:dependency_files) { project_dependency_files("bun/simple_v1") }

it "parses dependencies properly" do
expect(dependencies.find { |d| d.name == "fetch-factory" }).to have_attributes(
name: "fetch-factory",
version: "0.0.1"
)
expect(dependencies.find { |d| d.name == "etag" }).to have_attributes(
name: "etag",
version: "1.8.1"
)
expect(dependencies.length).to eq(17)
end
end
end
end
end
Loading

0 comments on commit eca15c1

Please sign in to comment.