From 97a8d226f957ded886605960a9344e70d8f085a6 Mon Sep 17 00:00:00 2001 From: dejan djenic Date: Fri, 20 Oct 2023 11:44:32 +0200 Subject: [PATCH 1/4] Implement GCP BigQuery --- Testcontainers.sln | 14 ++++ src/Testcontainers.BigQuery/.editorconfig | 1 + .../BigQueryBuilder.cs | 71 +++++++++++++++++++ .../BigQueryConfiguration.cs | 68 ++++++++++++++++++ .../BigQueryContainer.cs | 25 +++++++ .../Testcontainers.BigQuery.csproj | 13 ++++ src/Testcontainers.BigQuery/Usings.cs | 10 +++ .../.editorconfig | 1 + .../BigQueryContainerTest.cs | 63 ++++++++++++++++ .../Testcontainers.BigQuery.Tests.csproj | 18 +++++ tests/Testcontainers.BigQuery.Tests/Usings.cs | 8 +++ 11 files changed, 292 insertions(+) create mode 100644 src/Testcontainers.BigQuery/.editorconfig create mode 100644 src/Testcontainers.BigQuery/BigQueryBuilder.cs create mode 100644 src/Testcontainers.BigQuery/BigQueryConfiguration.cs create mode 100644 src/Testcontainers.BigQuery/BigQueryContainer.cs create mode 100644 src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj create mode 100644 src/Testcontainers.BigQuery/Usings.cs create mode 100644 tests/Testcontainers.BigQuery.Tests/.editorconfig create mode 100644 tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs create mode 100644 tests/Testcontainers.BigQuery.Tests/Testcontainers.BigQuery.Tests.csproj create mode 100644 tests/Testcontainers.BigQuery.Tests/Usings.cs diff --git a/Testcontainers.sln b/Testcontainers.sln index f2e0b0d53..64d8142b5 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -155,6 +155,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery", "src\Testcontainers.BigQuery\Testcontainers.BigQuery.csproj", "{A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery.Tests", "tests\Testcontainers.BigQuery.Tests\Testcontainers.BigQuery.Tests.csproj", "{03E60673-078A-4508-99AD-8537CE6F78F1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -448,6 +452,14 @@ Global {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.Build.0 = Release|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {3F2E254F-C203-43FD-A078-DC3E2CBC0F9F} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -521,5 +533,7 @@ Global {1A1983E6-5297-435F-B467-E8E1F11277D6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} + {03E60673-078A-4508-99AD-8537CE6F78F1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} EndGlobalSection EndGlobal diff --git a/src/Testcontainers.BigQuery/.editorconfig b/src/Testcontainers.BigQuery/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/src/Testcontainers.BigQuery/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/BigQueryBuilder.cs b/src/Testcontainers.BigQuery/BigQueryBuilder.cs new file mode 100644 index 000000000..147dff1f2 --- /dev/null +++ b/src/Testcontainers.BigQuery/BigQueryBuilder.cs @@ -0,0 +1,71 @@ +namespace Testcontainers.BigQuery; + +/// +[PublicAPI] +public sealed class BigQueryBuilder : ContainerBuilder +{ + public const string Image = "ghcr.io/goccy/bigquery-emulator"; + public const ushort BigQueryPort = 9050; + private string DefaultProjectId = "test"; + /// + /// Initializes a new instance of the class. + /// + public BigQueryBuilder() + : this(new BigQueryConfiguration()) + { + DockerResourceConfiguration = Init().DockerResourceConfiguration; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + private BigQueryBuilder(BigQueryConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + DockerResourceConfiguration = resourceConfiguration; + } + + protected override BigQueryConfiguration DockerResourceConfiguration { get; } + + /// + public override BigQueryContainer Build() + { + Validate(); + return new BigQueryContainer(DockerResourceConfiguration, TestcontainersSettings.Logger); + } + + /// + protected override BigQueryBuilder Clone(IResourceConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new BigQueryConfiguration(resourceConfiguration)); + } + + /// + protected override BigQueryBuilder Clone(IContainerConfiguration resourceConfiguration) + { + return Merge(DockerResourceConfiguration, new BigQueryConfiguration(resourceConfiguration)); + } + + /// + protected override BigQueryBuilder Merge(BigQueryConfiguration oldValue, BigQueryConfiguration newValue) + { + return new BigQueryBuilder(new BigQueryConfiguration(oldValue, newValue)); + } + + public BigQueryBuilder WithProject(string project) + { + return Merge(DockerResourceConfiguration, new BigQueryConfiguration(project)) + .WithCommand("--project",project); + } + + protected override BigQueryBuilder Init() + { + return base.Init() + .WithProject(DefaultProjectId) + .WithImage(Image) + .WithPortBinding(BigQueryPort, true) + .WithCommand("--project",DefaultProjectId) + .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*listening.*$")); + } +} \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/BigQueryConfiguration.cs b/src/Testcontainers.BigQuery/BigQueryConfiguration.cs new file mode 100644 index 000000000..893d19bd4 --- /dev/null +++ b/src/Testcontainers.BigQuery/BigQueryConfiguration.cs @@ -0,0 +1,68 @@ +namespace Testcontainers.BigQuery; + +/// +[PublicAPI] +public sealed class BigQueryConfiguration : ContainerConfiguration +{ + /// + /// Initializes a new instance of the class. + /// + /// The BigQuery config. + public BigQueryConfiguration(string project = null) + { + // // Sets the custom builder methods property values. + Project = project; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public BigQueryConfiguration(IResourceConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public BigQueryConfiguration(IContainerConfiguration resourceConfiguration) + : base(resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The Docker resource configuration. + public BigQueryConfiguration(BigQueryConfiguration resourceConfiguration) + : this(new BigQueryConfiguration(), resourceConfiguration) + { + // Passes the configuration upwards to the base implementations to create an updated immutable copy. + } + + /// + /// Initializes a new instance of the class. + /// + /// The old Docker resource configuration. + /// The new Docker resource configuration. + public BigQueryConfiguration(BigQueryConfiguration oldValue, BigQueryConfiguration newValue) + : base(oldValue, newValue) + { + // // Create an updated immutable copy of the module configuration. + Project = BuildConfiguration.Combine(oldValue.Project, newValue.Project); + } + + /// + /// Project + /// + public string Project { get; } + + // /// + // /// Gets the BigQuery config. + // /// + // public object Config { get; } +} \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/BigQueryContainer.cs b/src/Testcontainers.BigQuery/BigQueryContainer.cs new file mode 100644 index 000000000..933cbb411 --- /dev/null +++ b/src/Testcontainers.BigQuery/BigQueryContainer.cs @@ -0,0 +1,25 @@ +namespace Testcontainers.BigQuery; + +/// +[PublicAPI] +public sealed class BigQueryContainer : DockerContainer +{ + /// + /// Initializes a new instance of the class. + /// + /// The container configuration. + /// The logger. + public BigQueryContainer(BigQueryConfiguration configuration, ILogger logger) + : base(configuration, logger) + { + } + + /// + /// Gets the Firestore emulator endpoint. + /// + /// The Firestore emulator endpoint. + public string GetEmulatorEndpoint() + { + return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(BigQueryBuilder.BigQueryPort)).ToString(); + } +} \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj b/src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj new file mode 100644 index 000000000..4c05d521f --- /dev/null +++ b/src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj @@ -0,0 +1,13 @@ + + + netstandard2.0;netstandard2.1 + latest + + + + + + + + + \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/Usings.cs b/src/Testcontainers.BigQuery/Usings.cs new file mode 100644 index 000000000..f889bad0a --- /dev/null +++ b/src/Testcontainers.BigQuery/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using Docker.DotNet.Models; +global using DotNet.Testcontainers; +global using DotNet.Testcontainers.Builders; +global using DotNet.Testcontainers.Configurations; +global using DotNet.Testcontainers.Containers; +global using JetBrains.Annotations; +global using Microsoft.Extensions.Logging; \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/.editorconfig b/tests/Testcontainers.BigQuery.Tests/.editorconfig new file mode 100644 index 000000000..6f066619d --- /dev/null +++ b/tests/Testcontainers.BigQuery.Tests/.editorconfig @@ -0,0 +1 @@ +root = true \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs new file mode 100644 index 000000000..1508c1e3f --- /dev/null +++ b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs @@ -0,0 +1,63 @@ +namespace Testcontainers.BigQuery; + +public sealed class BigQueryContainerTest : IAsyncLifetime +{ + const string ProjectId = "test-test"; + private readonly BigQueryContainer _bigQueryContainer = new BigQueryBuilder().WithProject(ProjectId).Build(); + + public Task InitializeAsync() + { + return _bigQueryContainer.StartAsync(); + } + + public Task DisposeAsync() + { + return _bigQueryContainer.DisposeAsync().AsTask(); + } + + [Fact] + [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] + public async Task GetSnapshotReturnsSetDocument() + { + string dataSetName = "mydata"; + string dataTableName = "scores"; + + var firstDate = new DateTime(2000, 1, 14, 10, 30, 0, DateTimeKind.Utc); + + var bigQueryBuilder = new BigQueryClientBuilder(); + bigQueryBuilder.ProjectId = ProjectId; + bigQueryBuilder.BaseUri = _bigQueryContainer.GetEmulatorEndpoint(); + + var client = await bigQueryBuilder.BuildAsync() + .ConfigureAwait(false); + + // Create the dataset if it doesn't exist. + BigQueryDataset dataset = await client.GetOrCreateDatasetAsync(dataSetName); + + // Create the table if it doesn't exist. + BigQueryTable table = await dataset.GetOrCreateTableAsync(dataTableName, new TableSchemaBuilder + { + {"player", BigQueryDbType.String}, + {"gameStarted", BigQueryDbType.Timestamp}, + {"score", BigQueryDbType.Int64} + }.Build()); + + // Insert a single row. There are many other ways of inserting + // data into a table. + await table.InsertRowAsync(new BigQueryInsertRow + { + {"player", "Bob"}, + {"score", 85}, + {"gameStarted", firstDate} + }); + + + var data = await client.ExecuteQueryAsync($"select * from {ProjectId}.{dataSetName}.{dataTableName}",new BigQueryParameter[]{}); + + Assert.Single(data); + Assert.Equal(data.FirstOrDefault()!["player"],"Bob"); + Assert.Equal(data.FirstOrDefault()!["score"],(long)85); + Assert.Equal(data.FirstOrDefault()!["gameStarted"],firstDate); + + } +} \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/Testcontainers.BigQuery.Tests.csproj b/tests/Testcontainers.BigQuery.Tests/Testcontainers.BigQuery.Tests.csproj new file mode 100644 index 000000000..54568351c --- /dev/null +++ b/tests/Testcontainers.BigQuery.Tests/Testcontainers.BigQuery.Tests.csproj @@ -0,0 +1,18 @@ + + + net6.0 + false + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/Usings.cs b/tests/Testcontainers.BigQuery.Tests/Usings.cs new file mode 100644 index 000000000..029f902da --- /dev/null +++ b/tests/Testcontainers.BigQuery.Tests/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading.Tasks; +global using DotNet.Testcontainers.Commons; +global using Google.Cloud.BigQuery.V2; +global using Google.Cloud.BigQuery; +global using Xunit; \ No newline at end of file From 85126f19d8213fde436bc4d1a95caa87d7d19f9f Mon Sep 17 00:00:00 2001 From: Dejan Djenic Date: Mon, 6 Nov 2023 14:50:20 +0100 Subject: [PATCH 2/4] dummy commit --- tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs index 1508c1e3f..30a3dc3f3 100644 --- a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs +++ b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs @@ -42,8 +42,7 @@ public async Task GetSnapshotReturnsSetDocument() {"score", BigQueryDbType.Int64} }.Build()); - // Insert a single row. There are many other ways of inserting - // data into a table. + // Insert a single row. There are many other ways of inserting data into a table. await table.InsertRowAsync(new BigQueryInsertRow { {"player", "Bob"}, From f1403cde45f746a694460548313cbc29fe261160 Mon Sep 17 00:00:00 2001 From: Dejan Djenic Date: Thu, 9 Nov 2023 11:20:47 +0100 Subject: [PATCH 3/4] add empty credentials on ubuntu run --- .../BigQueryContainerTest.cs | 1 + .../EmptyCredentials.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs diff --git a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs index 30a3dc3f3..a8c756bdb 100644 --- a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs +++ b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs @@ -27,6 +27,7 @@ public async Task GetSnapshotReturnsSetDocument() var bigQueryBuilder = new BigQueryClientBuilder(); bigQueryBuilder.ProjectId = ProjectId; bigQueryBuilder.BaseUri = _bigQueryContainer.GetEmulatorEndpoint(); + bigQueryBuilder.Credential = new EmptyCredentials(); var client = await bigQueryBuilder.BuildAsync() .ConfigureAwait(false); diff --git a/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs b/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs new file mode 100644 index 000000000..8e79e50ec --- /dev/null +++ b/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs @@ -0,0 +1,20 @@ +using System.Threading; +using Google.Apis.Auth.OAuth2; +using Google.Apis.Http; + +namespace Testcontainers.BigQuery +{ + public class EmptyCredentials:ICredential + { + public void Initialize(ConfigurableHttpClient httpClient) + { + + } + + public async Task GetAccessTokenForRequestAsync(string authUri = null, + CancellationToken cancellationToken = new CancellationToken()) + { + return "empty"; + } + } +} \ No newline at end of file From c6beb1bbad70fdfd386b44ec9d47602a39bb06d1 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:46:11 +0100 Subject: [PATCH 4/4] chore: Algin BigQuery module --- Testcontainers.sln | 28 +++--- .../BigQueryBuilder.cs | 44 ++++++---- .../BigQueryConfiguration.cs | 17 +--- .../BigQueryContainer.cs | 6 +- src/Testcontainers.BigQuery/Usings.cs | 3 - .../BigQueryContainerTest.cs | 85 +++++++++++-------- .../EmptyCredentials.cs | 20 ----- tests/Testcontainers.BigQuery.Tests/Usings.cs | 7 +- 8 files changed, 98 insertions(+), 112 deletions(-) delete mode 100644 tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs diff --git a/Testcontainers.sln b/Testcontainers.sln index 77302c783..4ad8edab7 100644 --- a/Testcontainers.sln +++ b/Testcontainers.sln @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7164F1FB EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Azurite", "src\Testcontainers.Azurite\Testcontainers.Azurite.csproj", "{3F2E254F-C203-43FD-A078-DC3E2CBC0F9F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery", "src\Testcontainers.BigQuery\Testcontainers.BigQuery.csproj", "{A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse", "src\Testcontainers.ClickHouse\Testcontainers.ClickHouse.csproj", "{B061A78E-536E-4CA1-8401-234D5FBFBAB7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Consul", "src\Testcontainers.Consul\Testcontainers.Consul.csproj", "{51ED33B9-B688-401E-85F2-329D3C935BD1}" @@ -87,6 +89,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers", "src\Testc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Azurite.Tests", "tests\Testcontainers.Azurite.Tests\Testcontainers.Azurite.Tests.csproj", "{B272FDDE-5E01-425D-B9E1-10FF883DDAAA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery.Tests", "tests\Testcontainers.BigQuery.Tests\Testcontainers.BigQuery.Tests.csproj", "{03E60673-078A-4508-99AD-8537CE6F78F1}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ClickHouse.Tests", "tests\Testcontainers.ClickHouse.Tests\Testcontainers.ClickHouse.Tests.csproj", "{9D0A0B32-4921-400C-99CB-8650677E3E44}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Commons", "tests\Testcontainers.Commons\Testcontainers.Commons.csproj", "{2478673C-B063-469D-ABD1-0C3E0A25541B}" @@ -167,10 +171,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery", "src\Testcontainers.BigQuery\Testcontainers.BigQuery.csproj", "{A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.BigQuery.Tests", "tests\Testcontainers.BigQuery.Tests\Testcontainers.BigQuery.Tests.csproj", "{03E60673-078A-4508-99AD-8537CE6F78F1}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -184,6 +184,10 @@ Global {3F2E254F-C203-43FD-A078-DC3E2CBC0F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F2E254F-C203-43FD-A078-DC3E2CBC0F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F2E254F-C203-43FD-A078-DC3E2CBC0F9F}.Release|Any CPU.Build.0 = Release|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.Build.0 = Release|Any CPU {B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Debug|Any CPU.Build.0 = Debug|Any CPU {B061A78E-536E-4CA1-8401-234D5FBFBAB7}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -328,6 +332,10 @@ Global {B272FDDE-5E01-425D-B9E1-10FF883DDAAA}.Debug|Any CPU.Build.0 = Debug|Any CPU {B272FDDE-5E01-425D-B9E1-10FF883DDAAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {B272FDDE-5E01-425D-B9E1-10FF883DDAAA}.Release|Any CPU.Build.0 = Release|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.Build.0 = Release|Any CPU {9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D0A0B32-4921-400C-99CB-8650677E3E44}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D0A0B32-4921-400C-99CB-8650677E3E44}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -488,17 +496,10 @@ Global {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU - {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3}.Release|Any CPU.Build.0 = Release|Any CPU - {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {03E60673-078A-4508-99AD-8537CE6F78F1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {03E60673-078A-4508-99AD-8537CE6F78F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {3F2E254F-C203-43FD-A078-DC3E2CBC0F9F} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} + {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {B061A78E-536E-4CA1-8401-234D5FBFBAB7} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {51ED33B9-B688-401E-85F2-329D3C935BD1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {A724806F-8C94-4438-8011-04A9A1575318} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} @@ -535,6 +536,7 @@ Global {64A87DE5-29B0-4A54-9E74-560484D8C7C0} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {EC76857B-A3B8-4B7A-A1B0-8D867A4D1733} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} {B272FDDE-5E01-425D-B9E1-10FF883DDAAA} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} + {03E60673-078A-4508-99AD-8537CE6F78F1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {9D0A0B32-4921-400C-99CB-8650677E3E44} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {2478673C-B063-469D-ABD1-0C3E0A25541B} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {91B23679-A2A5-4132-8AFA-740CE40A88B6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} @@ -575,7 +577,5 @@ Global {1A1983E6-5297-435F-B467-E8E1F11277D6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} {EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} - {A9FF9C7F-BBA0-4B44-90B7-48A60F9E00F3} = {673F23AE-7694-4BB9-ABD4-136D6C13634E} - {03E60673-078A-4508-99AD-8537CE6F78F1} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF} EndGlobalSection EndGlobal diff --git a/src/Testcontainers.BigQuery/BigQueryBuilder.cs b/src/Testcontainers.BigQuery/BigQueryBuilder.cs index 147dff1f2..3d2d66288 100644 --- a/src/Testcontainers.BigQuery/BigQueryBuilder.cs +++ b/src/Testcontainers.BigQuery/BigQueryBuilder.cs @@ -4,9 +4,12 @@ namespace Testcontainers.BigQuery; [PublicAPI] public sealed class BigQueryBuilder : ContainerBuilder { - public const string Image = "ghcr.io/goccy/bigquery-emulator"; + public const string BigQueryImage = "ghcr.io/goccy/bigquery-emulator:0.4"; + public const ushort BigQueryPort = 9050; - private string DefaultProjectId = "test"; + + public const string DefaultProjectId = "default"; + /// /// Initializes a new instance of the class. /// @@ -26,8 +29,19 @@ private BigQueryBuilder(BigQueryConfiguration resourceConfiguration) DockerResourceConfiguration = resourceConfiguration; } + /// protected override BigQueryConfiguration DockerResourceConfiguration { get; } + /// + /// + /// + /// + /// + public BigQueryBuilder WithProject(string projectId) + { + return WithCommand("--project", projectId); + } + /// public override BigQueryContainer Build() { @@ -35,6 +49,16 @@ public override BigQueryContainer Build() return new BigQueryContainer(DockerResourceConfiguration, TestcontainersSettings.Logger); } + /// + protected override BigQueryBuilder Init() + { + return base.Init() + .WithImage(BigQueryImage) + .WithPortBinding(BigQueryPort, true) + .WithProject(DefaultProjectId) + .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*listening.*$")); + } + /// protected override BigQueryBuilder Clone(IResourceConfiguration resourceConfiguration) { @@ -52,20 +76,4 @@ protected override BigQueryBuilder Merge(BigQueryConfiguration oldValue, BigQuer { return new BigQueryBuilder(new BigQueryConfiguration(oldValue, newValue)); } - - public BigQueryBuilder WithProject(string project) - { - return Merge(DockerResourceConfiguration, new BigQueryConfiguration(project)) - .WithCommand("--project",project); - } - - protected override BigQueryBuilder Init() - { - return base.Init() - .WithProject(DefaultProjectId) - .WithImage(Image) - .WithPortBinding(BigQueryPort, true) - .WithCommand("--project",DefaultProjectId) - .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*listening.*$")); - } } \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/BigQueryConfiguration.cs b/src/Testcontainers.BigQuery/BigQueryConfiguration.cs index 893d19bd4..0e705d945 100644 --- a/src/Testcontainers.BigQuery/BigQueryConfiguration.cs +++ b/src/Testcontainers.BigQuery/BigQueryConfiguration.cs @@ -7,11 +7,8 @@ public sealed class BigQueryConfiguration : ContainerConfiguration /// /// Initializes a new instance of the class. /// - /// The BigQuery config. - public BigQueryConfiguration(string project = null) + public BigQueryConfiguration() { - // // Sets the custom builder methods property values. - Project = project; } /// @@ -52,17 +49,5 @@ public BigQueryConfiguration(BigQueryConfiguration resourceConfiguration) public BigQueryConfiguration(BigQueryConfiguration oldValue, BigQueryConfiguration newValue) : base(oldValue, newValue) { - // // Create an updated immutable copy of the module configuration. - Project = BuildConfiguration.Combine(oldValue.Project, newValue.Project); } - - /// - /// Project - /// - public string Project { get; } - - // /// - // /// Gets the BigQuery config. - // /// - // public object Config { get; } } \ No newline at end of file diff --git a/src/Testcontainers.BigQuery/BigQueryContainer.cs b/src/Testcontainers.BigQuery/BigQueryContainer.cs index 933cbb411..41936520e 100644 --- a/src/Testcontainers.BigQuery/BigQueryContainer.cs +++ b/src/Testcontainers.BigQuery/BigQueryContainer.cs @@ -13,11 +13,11 @@ public BigQueryContainer(BigQueryConfiguration configuration, ILogger logger) : base(configuration, logger) { } - + /// - /// Gets the Firestore emulator endpoint. + /// Gets the BigQuery emulator endpoint. /// - /// The Firestore emulator endpoint. + /// The BigQuery emulator endpoint. public string GetEmulatorEndpoint() { return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(BigQueryBuilder.BigQueryPort)).ToString(); diff --git a/src/Testcontainers.BigQuery/Usings.cs b/src/Testcontainers.BigQuery/Usings.cs index f889bad0a..bf2829a65 100644 --- a/src/Testcontainers.BigQuery/Usings.cs +++ b/src/Testcontainers.BigQuery/Usings.cs @@ -1,8 +1,5 @@ global using System; -global using System.Collections.Generic; -global using System.Linq; global using Docker.DotNet.Models; -global using DotNet.Testcontainers; global using DotNet.Testcontainers.Builders; global using DotNet.Testcontainers.Configurations; global using DotNet.Testcontainers.Containers; diff --git a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs index a8c756bdb..0d8a72491 100644 --- a/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs +++ b/tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs @@ -2,8 +2,7 @@ namespace Testcontainers.BigQuery; public sealed class BigQueryContainerTest : IAsyncLifetime { - const string ProjectId = "test-test"; - private readonly BigQueryContainer _bigQueryContainer = new BigQueryBuilder().WithProject(ProjectId).Build(); + private readonly BigQueryContainer _bigQueryContainer = new BigQueryBuilder().Build(); public Task InitializeAsync() { @@ -17,47 +16,63 @@ public Task DisposeAsync() [Fact] [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))] - public async Task GetSnapshotReturnsSetDocument() + public async Task ExecuteQueryReturnsInsertRow() { - string dataSetName = "mydata"; - string dataTableName = "scores"; - - var firstDate = new DateTime(2000, 1, 14, 10, 30, 0, DateTimeKind.Utc); + // Given + var utcNow = DateTime.UtcNow; - var bigQueryBuilder = new BigQueryClientBuilder(); - bigQueryBuilder.ProjectId = ProjectId; - bigQueryBuilder.BaseUri = _bigQueryContainer.GetEmulatorEndpoint(); - bigQueryBuilder.Credential = new EmptyCredentials(); + // Storing DateTime.UtcNow in BigQuery loses precision. The query result differs + // in the last digit. Truncating milliseconds prevents running into this issue. + var utcNowWithoutMilliseconds = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, utcNow.Hour, utcNow.Minute, utcNow.Second, DateTimeKind.Utc); - var client = await bigQueryBuilder.BuildAsync() + var bigQueryClientBuilder = new BigQueryClientBuilder(); + bigQueryClientBuilder.BaseUri = _bigQueryContainer.GetEmulatorEndpoint(); + bigQueryClientBuilder.ProjectId = BigQueryBuilder.DefaultProjectId; + bigQueryClientBuilder.Credential = new Credential(); + + var tableSchemaBuilder = new TableSchemaBuilder(); + tableSchemaBuilder.Add("player", BigQueryDbType.String); + tableSchemaBuilder.Add("gameStarted", BigQueryDbType.DateTime); + tableSchemaBuilder.Add("score", BigQueryDbType.Int64); + var tableSchema = tableSchemaBuilder.Build(); + + var expectedRow = new BigQueryInsertRow(); + expectedRow.Add("player", "Bob"); + expectedRow.Add("gameStarted", utcNowWithoutMilliseconds); + expectedRow.Add("score", 85L); + + using var bigQueryClient = await bigQueryClientBuilder.BuildAsync() .ConfigureAwait(false); - // Create the dataset if it doesn't exist. - BigQueryDataset dataset = await client.GetOrCreateDatasetAsync(dataSetName); + var dataset = await bigQueryClient.GetOrCreateDatasetAsync("mydata") + .ConfigureAwait(false); - // Create the table if it doesn't exist. - BigQueryTable table = await dataset.GetOrCreateTableAsync(dataTableName, new TableSchemaBuilder + // When + var table = await dataset.CreateTableAsync("scores", tableSchema) + .ConfigureAwait(false); + + _ = await table.InsertRowAsync(expectedRow) + .ConfigureAwait(false); + + var results = await bigQueryClient.ExecuteQueryAsync($"SELECT * FROM {table}", null) + .ConfigureAwait(false); + + // Then + Assert.Single(results); + Assert.Equal(expectedRow["player"], results.Single()["player"]); + Assert.Equal(expectedRow["gameStarted"], results.Single()["gameStarted"]); + Assert.Equal(expectedRow["score"], results.Single()["score"]); + } + + private sealed class Credential : ICredential + { + public void Initialize(ConfigurableHttpClient httpClient) { - {"player", BigQueryDbType.String}, - {"gameStarted", BigQueryDbType.Timestamp}, - {"score", BigQueryDbType.Int64} - }.Build()); + } - // Insert a single row. There are many other ways of inserting data into a table. - await table.InsertRowAsync(new BigQueryInsertRow + public Task GetAccessTokenForRequestAsync(string authUri, CancellationToken cancellationToken) { - {"player", "Bob"}, - {"score", 85}, - {"gameStarted", firstDate} - }); - - - var data = await client.ExecuteQueryAsync($"select * from {ProjectId}.{dataSetName}.{dataTableName}",new BigQueryParameter[]{}); - - Assert.Single(data); - Assert.Equal(data.FirstOrDefault()!["player"],"Bob"); - Assert.Equal(data.FirstOrDefault()!["score"],(long)85); - Assert.Equal(data.FirstOrDefault()!["gameStarted"],firstDate); - + return Task.FromResult(string.Empty); + } } } \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs b/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs deleted file mode 100644 index 8e79e50ec..000000000 --- a/tests/Testcontainers.BigQuery.Tests/EmptyCredentials.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Threading; -using Google.Apis.Auth.OAuth2; -using Google.Apis.Http; - -namespace Testcontainers.BigQuery -{ - public class EmptyCredentials:ICredential - { - public void Initialize(ConfigurableHttpClient httpClient) - { - - } - - public async Task GetAccessTokenForRequestAsync(string authUri = null, - CancellationToken cancellationToken = new CancellationToken()) - { - return "empty"; - } - } -} \ No newline at end of file diff --git a/tests/Testcontainers.BigQuery.Tests/Usings.cs b/tests/Testcontainers.BigQuery.Tests/Usings.cs index 029f902da..e8c0374a0 100644 --- a/tests/Testcontainers.BigQuery.Tests/Usings.cs +++ b/tests/Testcontainers.BigQuery.Tests/Usings.cs @@ -1,8 +1,9 @@ global using System; -global using System.Collections.Generic; global using System.Linq; global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; global using Google.Cloud.BigQuery.V2; -global using Google.Cloud.BigQuery; -global using Xunit; \ No newline at end of file +global using Xunit; +global using System.Threading; +global using Google.Apis.Auth.OAuth2; +global using Google.Apis.Http; \ No newline at end of file