1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-19 00:39:37 +01:00
nix/src/libflake-tests/flakeref.cc
2025-10-14 23:48:45 -04:00

264 lines
9.1 KiB
C++

#include <gtest/gtest.h>
#include <set>
#include <string>
#include <utility>
#include "nix/fetchers/fetch-settings.hh"
#include "nix/flake/flakeref.hh"
#include "nix/fetchers/attrs.hh"
#include "nix/fetchers/fetchers.hh"
#include "nix/util/configuration.hh"
#include "nix/util/error.hh"
#include "nix/util/experimental-features.hh"
namespace nix {
/* ----------- tests for flake/flakeref.hh --------------------------------------------------*/
TEST(parseFlakeRef, path)
{
experimentalFeatureSettings.experimentalFeatures.get().insert(Xp::Flakes);
fetchers::Settings fetchSettings;
{
auto s = "/foo/bar";
auto flakeref = parseFlakeRef(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar");
}
{
auto s = "/foo/bar?revCount=123&rev=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
auto flakeref = parseFlakeRef(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar?rev=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&revCount=123");
}
{
auto s = "/foo/bar?xyzzy=123";
EXPECT_THROW(parseFlakeRef(fetchSettings, s), Error);
}
{
auto s = "/foo/bar#bla";
EXPECT_THROW(parseFlakeRef(fetchSettings, s), Error);
}
{
auto s = "/foo/bar#bla";
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar");
ASSERT_EQ(fragment, "bla");
}
{
auto s = "/foo/bar?revCount=123#bla";
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar?revCount=123");
ASSERT_EQ(fragment, "bla");
}
{
auto s = "/foo bar/baz?dir=bla space";
auto flakeref = parseFlakeRef(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "path:/foo%20bar/baz?dir=bla%20space");
ASSERT_EQ(flakeref.toAttrs().at("dir"), fetchers::Attr("bla space"));
}
}
TEST(parseFlakeRef, GitArchiveInput)
{
experimentalFeatureSettings.experimentalFeatures.get().insert(Xp::Flakes);
fetchers::Settings fetchSettings;
{
auto s = "github:foo/bar/branch%23"; // branch name with `#`
auto flakeref = parseFlakeRef(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "github:foo/bar/branch%23");
}
{
auto s = "github:foo/bar?ref=branch%23"; // branch name with `#`
auto flakeref = parseFlakeRef(fetchSettings, s);
ASSERT_EQ(flakeref.to_string(), "github:foo/bar/branch%23");
}
{
auto s = "github:foo/bar?ref=branch#\"name.with.dot\""; // unescaped quotes `"`
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
ASSERT_EQ(fragment, "\"name.with.dot\"");
ASSERT_EQ(flakeref.to_string(), "github:foo/bar/branch");
}
{
auto s = "github:foo/bar#\"name.with.dot\""; // unescaped quotes `"`
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
ASSERT_EQ(fragment, "\"name.with.dot\"");
ASSERT_EQ(flakeref.to_string(), "github:foo/bar");
}
}
struct InputFromURLTestCase
{
std::string url;
fetchers::Attrs attrs;
std::string description;
std::string expectedUrl = url;
};
class InputFromURLTest : public ::testing::WithParamInterface<InputFromURLTestCase>, public ::testing::Test
{};
TEST_P(InputFromURLTest, attrsAreCorrectAndRoundTrips)
{
experimentalFeatureSettings.experimentalFeatures.get().insert(Xp::Flakes);
fetchers::Settings fetchSettings;
const auto & testCase = GetParam();
auto flakeref = parseFlakeRef(fetchSettings, testCase.url);
EXPECT_EQ(flakeref.toAttrs(), testCase.attrs);
EXPECT_EQ(flakeref.to_string(), testCase.expectedUrl);
auto input = fetchers::Input::fromURL(fetchSettings, flakeref.to_string());
EXPECT_EQ(input.toURLString(), testCase.expectedUrl);
EXPECT_EQ(input.toAttrs(), testCase.attrs);
// Round-trip check.
auto input2 = fetchers::Input::fromURL(fetchSettings, input.toURLString());
EXPECT_EQ(input, input2);
EXPECT_EQ(input.toURLString(), input2.toURLString());
}
using fetchers::Attr;
INSTANTIATE_TEST_SUITE_P(
InputFromURL,
InputFromURLTest,
::testing::Values(
InputFromURLTestCase{
.url = "flake:nixpkgs",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
},
.description = "basic_indirect",
},
InputFromURLTestCase{
.url = "flake:nixpkgs/branch",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
},
.description = "basic_indirect_branch",
},
InputFromURLTestCase{
.url = "nixpkgs/branch",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
},
.description = "flake_id_ref_branch",
.expectedUrl = "flake:nixpkgs/branch",
},
InputFromURLTestCase{
.url = "nixpkgs/branch/2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
{"rev", Attr("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")},
},
.description = "flake_id_ref_branch_trailing_slash",
.expectedUrl = "flake:nixpkgs/branch/2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
},
// The following tests are for back-compat with lax parsers in older versions
// that used `tokenizeString` for splitting path segments, which ignores empty
// strings.
InputFromURLTestCase{
.url = "nixpkgs/branch////",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
},
.description = "flake_id_ref_branch_ignore_empty_trailing_segments",
.expectedUrl = "flake:nixpkgs/branch",
},
InputFromURLTestCase{
.url = "nixpkgs/branch///2aae6c35c94fcfb415dbe95f408b9ce91ee846ed///",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
{"rev", Attr("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")},
},
.description = "flake_id_ref_branch_ignore_empty_segments_ref_rev",
.expectedUrl = "flake:nixpkgs/branch/2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
},
InputFromURLTestCase{
// Note that this is different from above because the "flake id" shorthand
// doesn't allow this.
.url = "flake:/nixpkgs///branch////",
.attrs =
{
{"id", Attr("nixpkgs")},
{"type", Attr("indirect")},
{"ref", Attr("branch")},
},
.description = "indirect_branch_empty_segments_everywhere",
.expectedUrl = "flake:nixpkgs/branch",
},
InputFromURLTestCase{
// TODO: Technically this has an empty authority, but it's ignored
// for now. Yes, this is what all versions going back to at least
// 2.18 did and yes, this should not be allowed.
.url = "github://////owner%42/////repo%41///branch%43////",
.attrs =
{
{"type", Attr("github")},
{"owner", Attr("ownerB")},
{"repo", Attr("repoA")},
{"ref", Attr("branchC")},
},
.description = "github_ref_slashes_in_path_everywhere",
.expectedUrl = "github:ownerB/repoA/branchC",
},
InputFromURLTestCase{
// FIXME: Subgroups in gitlab URLs are busted. This double-encoding
// behavior exists since 2.18. See issue #9161 and PR #8845.
.url = "gitlab:/owner%252Fsubgroup/////repo%41///branch%43////",
.attrs =
{
{"type", Attr("gitlab")},
{"owner", Attr("owner%2Fsubgroup")},
{"repo", Attr("repoA")},
{"ref", Attr("branchC")},
},
.description = "gitlab_ref_slashes_in_path_everywhere_with_pct_encoding",
.expectedUrl = "gitlab:owner%252Fsubgroup/repoA/branchC",
}),
[](const ::testing::TestParamInfo<InputFromURLTestCase> & info) { return info.param.description; });
TEST(to_string, doesntReencodeUrl)
{
fetchers::Settings fetchSettings;
auto s = "http://localhost:8181/test/+3d.tar.gz";
auto flakeref = parseFlakeRef(fetchSettings, s);
auto unparsed = flakeref.to_string();
auto expected = "http://localhost:8181/test/%2B3d.tar.gz";
ASSERT_EQ(unparsed, expected);
}
} // namespace nix