1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-19 16:59:35 +01:00

libstore: Introduce ParsedS3URL type

This systematizes the way our s3:// URLs are parsed in filetransfer.cc.
Yoinked out and refactored out of [1].

[1]: https://github.com/NixOS/nix/pull/13752

Co-authored-by: Bernardo Meurer Costa <beme@anthropic.com>
This commit is contained in:
Sergei Zimmerman 2025-08-19 23:21:36 +03:00
parent 5c0eff24d5
commit 69fcc2cfc1
No known key found for this signature in database
8 changed files with 212 additions and 28 deletions

96
src/libstore-tests/s3.cc Normal file
View file

@ -0,0 +1,96 @@
#include "nix/store/s3.hh"
#include "nix/util/tests/gmock-matchers.hh"
#if NIX_WITH_S3_SUPPORT
# include <gtest/gtest.h>
# include <gmock/gmock.h>
namespace nix {
struct ParsedS3URLTestCase
{
std::string url;
ParsedS3URL expected;
std::string description;
};
class ParsedS3URLTest : public ::testing::WithParamInterface<ParsedS3URLTestCase>, public ::testing::Test
{};
TEST_P(ParsedS3URLTest, parseS3URLSuccessfully)
{
const auto & testCase = GetParam();
auto parsed = ParsedS3URL::parse(testCase.url);
ASSERT_EQ(parsed, testCase.expected);
}
INSTANTIATE_TEST_SUITE_P(
QueryParams,
ParsedS3URLTest,
::testing::Values(
ParsedS3URLTestCase{
"s3://my-bucket/my-key.txt",
{
.bucket = "my-bucket",
.key = "my-key.txt",
},
"basic_s3_bucket"},
ParsedS3URLTestCase{
"s3://prod-cache/nix/store/abc123.nar.xz?region=eu-west-1",
{
.bucket = "prod-cache",
.key = "nix/store/abc123.nar.xz",
.region = "eu-west-1",
},
"with_region"},
ParsedS3URLTestCase{
"s3://bucket/key?region=us-west-2&profile=prod&endpoint=custom.s3.com&scheme=https&region=us-east-1",
{
.bucket = "bucket",
.key = "key",
.profile = "prod",
.region = "us-west-2", //< using the first parameter (decodeQuery ignores dupicates)
.scheme = "https",
.endpoint = ParsedURL::Authority{.host = "custom.s3.com"},
},
"complex"},
ParsedS3URLTestCase{
"s3://cache/file.txt?profile=production&region=ap-southeast-2",
{
.bucket = "cache",
.key = "file.txt",
.profile = "production",
.region = "ap-southeast-2",
},
"with_profile_and_region"},
ParsedS3URLTestCase{
"s3://bucket/key?endpoint=https://minio.local&scheme=http",
{
.bucket = "bucket",
.key = "key",
/* TODO: Figure out what AWS SDK is doing when both endpointOverride and scheme are set. */
.scheme = "http",
.endpoint =
ParsedURL{
.scheme = "https",
.authority = ParsedURL::Authority{.host = "minio.local"},
},
},
"with_absolute_endpoint_uri"}),
[](const ::testing::TestParamInfo<ParsedS3URLTestCase> & info) { return info.param.description; });
TEST(InvalidParsedS3URLTest, parseS3URLErrors)
{
auto invalidBucketMatcher = ::testing::ThrowsMessage<BadURL>(
testing::HasSubstrIgnoreANSIMatcher("error: URI has a missing or invalid bucket name"));
/* Empty bucket (authority) */
ASSERT_THAT([]() { ParsedS3URL::parse("s3:///key"); }, invalidBucketMatcher);
/* Invalid bucket name */
ASSERT_THAT([]() { ParsedS3URL::parse("s3://127.0.0.1"); }, invalidBucketMatcher);
}
} // namespace nix
#endif