Skip to content

Commit

Permalink
feat: Add BigQuery module (#1034)
Browse files Browse the repository at this point in the history
Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
  • Loading branch information
dejandjenic and HofmeisterAn authored Nov 21, 2023
1 parent ab2c7c9 commit 33ece8e
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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}"
Expand Down Expand Up @@ -180,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
Expand Down Expand Up @@ -324,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
Expand Down Expand Up @@ -487,6 +499,7 @@ Global
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}
Expand Down Expand Up @@ -523,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}
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.BigQuery/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
79 changes: 79 additions & 0 deletions src/Testcontainers.BigQuery/BigQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
namespace Testcontainers.BigQuery;

/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class BigQueryBuilder : ContainerBuilder<BigQueryBuilder, BigQueryContainer, BigQueryConfiguration>
{
public const string BigQueryImage = "ghcr.io/goccy/bigquery-emulator:0.4";

public const ushort BigQueryPort = 9050;

public const string DefaultProjectId = "default";

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryBuilder" /> class.
/// </summary>
public BigQueryBuilder()
: this(new BigQueryConfiguration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryBuilder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private BigQueryBuilder(BigQueryConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override BigQueryConfiguration DockerResourceConfiguration { get; }

/// <summary>
///
/// </summary>
/// <param name="projectId"></param>
/// <returns></returns>
public BigQueryBuilder WithProject(string projectId)
{
return WithCommand("--project", projectId);
}

/// <inheritdoc />
public override BigQueryContainer Build()
{
Validate();
return new BigQueryContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
protected override BigQueryBuilder Init()
{
return base.Init()
.WithImage(BigQueryImage)
.WithPortBinding(BigQueryPort, true)
.WithProject(DefaultProjectId)
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("(?s).*listening.*$"));
}

/// <inheritdoc />
protected override BigQueryBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new BigQueryConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override BigQueryBuilder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new BigQueryConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override BigQueryBuilder Merge(BigQueryConfiguration oldValue, BigQueryConfiguration newValue)
{
return new BigQueryBuilder(new BigQueryConfiguration(oldValue, newValue));
}
}
53 changes: 53 additions & 0 deletions src/Testcontainers.BigQuery/BigQueryConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Testcontainers.BigQuery;

/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class BigQueryConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="BigQueryConfiguration" /> class.
/// </summary>
public BigQueryConfiguration()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public BigQueryConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public BigQueryConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public BigQueryConfiguration(BigQueryConfiguration resourceConfiguration)
: this(new BigQueryConfiguration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="BigQueryConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public BigQueryConfiguration(BigQueryConfiguration oldValue, BigQueryConfiguration newValue)
: base(oldValue, newValue)
{
}
}
25 changes: 25 additions & 0 deletions src/Testcontainers.BigQuery/BigQueryContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Testcontainers.BigQuery;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class BigQueryContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="BigQueryContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public BigQueryContainer(BigQueryConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
}

/// <summary>
/// Gets the BigQuery emulator endpoint.
/// </summary>
/// <returns>The BigQuery emulator endpoint.</returns>
public string GetEmulatorEndpoint()
{
return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(BigQueryBuilder.BigQueryPort)).ToString();
}
}
13 changes: 13 additions & 0 deletions src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions src/Testcontainers.BigQuery/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global using System;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using JetBrains.Annotations;
global using Microsoft.Extensions.Logging;
1 change: 1 addition & 0 deletions tests/Testcontainers.BigQuery.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
78 changes: 78 additions & 0 deletions tests/Testcontainers.BigQuery.Tests/BigQueryContainerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace Testcontainers.BigQuery;

public sealed class BigQueryContainerTest : IAsyncLifetime
{
private readonly BigQueryContainer _bigQueryContainer = new BigQueryBuilder().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 ExecuteQueryReturnsInsertRow()
{
// Given
var utcNow = DateTime.UtcNow;

// 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 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);

var dataset = await bigQueryClient.GetOrCreateDatasetAsync("mydata")
.ConfigureAwait(false);

// 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)
{
}

public Task<string> GetAccessTokenForRequestAsync(string authUri, CancellationToken cancellationToken)
{
return Task.FromResult(string.Empty);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0"/>
<PackageReference Include="xunit" Version="2.5.0"/>
<PackageReference Include="Google.Cloud.BigQuery.V2" Version="3.4.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers.BigQuery/Testcontainers.BigQuery.csproj"/>
<ProjectReference Include="$(SolutionDir)tests/Testcontainers.Commons/Testcontainers.Commons.csproj"/>
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions tests/Testcontainers.BigQuery.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global using System;
global using System.Linq;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
global using Google.Cloud.BigQuery.V2;
global using Xunit;
global using System.Threading;
global using Google.Apis.Auth.OAuth2;
global using Google.Apis.Http;

0 comments on commit 33ece8e

Please sign in to comment.