Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using Google.Cloud.EntityFrameworkCore.Spanner.Storage;

Expand All @@ -26,7 +27,7 @@ public Albums()

public long AlbumId { get; set; }
public string Title { get; set; }
public SpannerDate? ReleaseDate { get; set; }
public DateOnly? ReleaseDate { get; set; }
public long SingerId { get; set; }
public HashSet<string> Awards { get; set; } = new ();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Singers()
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public DateOnly? BirthDate { get; set; }
public SpannerDate? BirthDate { get; set; }
public byte[] Picture { get; set; }

public virtual ICollection<Albums> Albums { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

using System;
using System.Collections.Generic;
using Google.Cloud.EntityFrameworkCore.Spanner.Storage;
using Google.Cloud.Spanner.V1;
using System.Text.Json;
using SpannerDate = Google.Cloud.EntityFrameworkCore.Spanner.Storage.SpannerDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public AutoGeneratedPrimaryKeyMockServerTests(SpannerMockServerFixture service)
service.SpannerMock.Reset();
}

private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";
private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port};UsePlainText=true";

[Fact]
public async Task FindInvoiceAsync_ReturnsNull_IfNotFound()
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Google.Cloud.Spanner.DataProvider;
using Google.Rpc;
using Xunit;
using SpannerDate = Google.Cloud.EntityFrameworkCore.Spanner.Storage.SpannerDate;
using V1 = Google.Cloud.Spanner.V1;
Expand Down Expand Up @@ -92,7 +94,13 @@ public EntityFrameworkMockUsingMutationsServerTests(SpannerMockServerFixture ser
service.SpannerMock.Reset();
}

private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";
private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port};UsePlainText=true";
//private string ConnectionString => $"{_fixture.Host}:{_fixture.Port}/projects/p1/instances/i1/databases/d1;usePlainText=true";

bool UsesClientLib()
{
return Environment.GetEnvironmentVariable("USE_CLIENT_LIB") == "true";
}

[Fact]
public async Task InsertAlbum()
Expand Down Expand Up @@ -132,6 +140,7 @@ public async Task InsertSinger()
var selectFullNameSql = AddSelectSingerFullNameResult("Alice Morrison", 0);

await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
db.Singers.Add(new Singers
{
SingerId = 1L,
Expand Down Expand Up @@ -251,6 +260,7 @@ public async Task UpdateSinger_SelectsFullName()
var selectFullNameSql = AddSelectSingerFullNameResult("Alice Pieterson-Morrison", 0);

await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
var singer = await db.Singers.FindAsync(1L);
Assert.NotNull(singer);
singer.LastName = "Pieterson-Morrison";
Expand All @@ -270,12 +280,12 @@ public async Task UpdateSinger_SelectsFullName()
request =>
{
Assert.Equal(selectSingerSql.Trim(), request.Sql.Trim());
Assert.Null(request.Transaction?.Id);
Assert.True(request.Transaction?.Id?.IsEmpty);
},
request =>
{
Assert.Equal(selectFullNameSql.Trim(), request.Sql.Trim());
Assert.Null(request.Transaction?.Id);
Assert.True(request.Transaction?.Id?.IsEmpty);
}
);
Assert.Collection(
Expand Down Expand Up @@ -481,8 +491,16 @@ public async Task ExplicitAndImplicitTransactionIsRetried(bool disableInternalRe
if (disableInternalRetries && useExplicitTransaction)
{
await db.SaveChangesAsync();
var e = await Assert.ThrowsAsync<SpannerException>(() => transaction.CommitAsync());
Assert.Equal(ErrorCode.Aborted, e.ErrorCode);
if (UsesClientLib())
{
var e = await Assert.ThrowsAsync<SpannerException>(() => transaction.CommitAsync());
Assert.Equal(ErrorCode.Aborted, e.ErrorCode);
}
else
{
var e = await Assert.ThrowsAsync<SpannerDbException>(() => transaction.CommitAsync());
Assert.Equal((int) Code.Aborted, e.Status.Code);
}
}
else
{
Expand Down Expand Up @@ -516,6 +534,7 @@ public async Task ExplicitAndImplicitTransactionIsRetried(bool disableInternalRe
public async Task CanInsertCommitTimestamp()
{
await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
_fixture.SpannerMock.AddOrUpdateStatementResult($"{Environment.NewLine}SELECT `ColComputed`" +
$"{Environment.NewLine}FROM `TableWithAllColumnTypes`{Environment.NewLine}WHERE TRUE AND `ColInt64` = @p0", StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.String }, "FOO"));

Expand All @@ -541,6 +560,7 @@ public async Task CanInsertCommitTimestamp()
public async Task CanUpdateCommitTimestamp()
{
await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
_fixture.SpannerMock.AddOrUpdateStatementResult($"{Environment.NewLine}SELECT `ColComputed`{Environment.NewLine}FROM `TableWithAllColumnTypes`{Environment.NewLine}WHERE TRUE AND `ColInt64` = @p0", StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.String }, "FOO"));

var row = new TableWithAllColumnTypes { ColInt64 = 1L };
Expand All @@ -567,6 +587,7 @@ public async Task CanUpdateCommitTimestamp()
public async Task CanInsertRowWithCommitTimestampAndComputedColumn()
{
await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
var selectSql = $"{Environment.NewLine}SELECT `ColComputed`{Environment.NewLine}FROM `TableWithAllColumnTypes`{Environment.NewLine}WHERE TRUE AND `ColInt64` = @p0";
_fixture.SpannerMock.AddOrUpdateStatementResult(selectSql, StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.String }, "FOO"));

Expand Down Expand Up @@ -607,6 +628,7 @@ public async Task CanInsertRowWithCommitTimestampAndComputedColumn()
public async Task CanInsertAllTypes()
{
await using var db = new MockServerSampleDbContextUsingMutations(ConnectionString);
await db.Database.OpenConnectionAsync();
_fixture.SpannerMock.AddOrUpdateStatementResult($"{Environment.NewLine}SELECT `ColComputed`" +
$"{Environment.NewLine}FROM `TableWithAllColumnTypes`{Environment.NewLine}WHERE TRUE AND `ColInt64` = @p0", StatementResult.CreateSingleColumnResultSet(new V1.Type { Code = V1.TypeCode.String }, "FOO"));

Expand Down Expand Up @@ -764,13 +786,13 @@ public async Task CanInsertAllTypes()
Assert.Empty(values[index++].ListValue.Values);
Assert.Equal("ColTimestamp", columns[index]);
Assert.Equal(Value.KindOneofCase.StringValue, values[index].KindCase);
Assert.Equal("2000-01-01T00:00:00Z", values[index++].StringValue);
Assert.Equal("2000-01-01T00:00:00.0000000Z", values[index++].StringValue);
Assert.Equal("ColTimestampArray", columns[index]);
Assert.Equal(Value.KindOneofCase.ListValue, values[index].KindCase);
Assert.Collection(values[index].ListValue.Values,
v => Assert.Equal("2000-01-01T00:00:00.001Z", v.StringValue),
v => Assert.Equal("2000-01-01T00:00:00.0010000Z", v.StringValue),
v => Assert.Equal(Value.KindOneofCase.NullValue, v.KindCase),
v => Assert.Equal("2000-01-01T00:00:00.002Z", v.StringValue)
v => Assert.Equal("2000-01-01T00:00:00.0020000Z", v.StringValue)
);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public MigrationMockServerTests(SpannerMockServerFixture service)
service.SpannerMock.Reset();
}

private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";
private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port};UsePlainText=true";
//private string ConnectionString => $"{_fixture.Host}:{_fixture.Port}/projects/p1/instances/i1/databases/d1;usePlainText=true";

[Fact]
public void TestMigrateUsesDdlBatch()
Expand All @@ -52,6 +53,7 @@ public void TestMigrateUsesDdlBatch()
StatementResult.CreateUpdateCount(1)
);
using var db = new MockMigrationSampleDbContext(ConnectionString);
db.Database.OpenConnection();
db.Database.Migrate();

Assert.Collection(_fixture.DatabaseAdminMock.Requests,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ public void Dispose()
}
}

private static readonly string s_dialect_query =
"select option_value from information_schema.database_options where option_name='database_dialect'";
private static readonly Empty s_empty = new ();
private static readonly TransactionOptions s_singleUse = new() { ReadOnly = new TransactionOptions.Types.ReadOnly { Strong = true, ReturnReadTimestamp = false } };

Expand All @@ -326,6 +328,11 @@ public void Dispose()
private bool _abortNextStatement;
private readonly ConcurrentDictionary<string, ExecutionTime> _executionTimes = new();

public MockSpannerService()
{
AddDialectResult();
}

public void AddOrUpdateStatementResult(string sql, StatementResult result)
{
_results.AddOrUpdate(sql.Trim(),
Expand All @@ -342,6 +349,20 @@ public void AddOrUpdateExecutionTime(string method, ExecutionTime executionTime)
);
}

private void AddDialectResult()
{
AddOrUpdateStatementResult(s_dialect_query,
StatementResult.CreateResultSet(
new List<Tuple<V1.TypeCode, string>>
{
Tuple.Create(V1.TypeCode.String, "option_value"),
},
new List<object[]>
{
new object[] { "GOOGLE_STANDARD_SQL" },
}));
}

internal void AbortTransaction(TransactionId transactionId)
{
var prop = transactionId.GetType().GetProperty("Id", BindingFlags.Instance | BindingFlags.NonPublic);
Expand Down Expand Up @@ -372,6 +393,7 @@ public void Reset()
_results.Clear();
_abortedTransactions.Clear();
_abortNextStatement = false;
AddDialectResult();
}

public override Task<Transaction> BeginTransaction(BeginTransactionRequest request, ServerCallContext context)
Expand Down Expand Up @@ -413,10 +435,14 @@ public override Task<Empty> Rollback(RollbackRequest request, ServerCallContext
return Task.FromResult(s_empty);
}

private Session CreateSession(DatabaseName database)
private Session CreateSession(DatabaseName database, bool multiplexed)
{
var id = Interlocked.Increment(ref _sessionCounter);
Session session = new Session { SessionName = new SessionName(database.ProjectId, database.InstanceId, database.DatabaseId, $"session-{id}") };
Session session = new Session
{
SessionName = new SessionName(database.ProjectId, database.InstanceId, database.DatabaseId, $"session-{id}"),
Multiplexed = multiplexed,
};
if (!_sessions.TryAdd(session.SessionName, session))
{
throw new RpcException(new Grpc.Core.Status(StatusCode.AlreadyExists, $"Session with id session-{id} already exists"));
Expand Down Expand Up @@ -494,7 +520,7 @@ private Transaction BeginTransaction(SessionName session, TransactionOptions opt
tx.Id = ByteString.CopyFromUtf8($"{session}/transactions/{id}");
if (options.ModeCase == TransactionOptions.ModeOneofCase.ReadOnly && options.ReadOnly.ReturnReadTimestamp)
{
tx.ReadTimestamp = Timestamp.FromDateTime(DateTime.Now);
tx.ReadTimestamp = Timestamp.FromDateTime(DateTime.UtcNow);
}
if (!singleUse)
{
Expand All @@ -515,7 +541,7 @@ public override Task<BatchCreateSessionsResponse> BatchCreateSessions(BatchCreat
BatchCreateSessionsResponse response = new BatchCreateSessionsResponse();
for (int i = 0; i < request.SessionCount; i++)
{
response.Session.Add(CreateSession(database));
response.Session.Add(CreateSession(database, false));
}
return Task.FromResult(response);
}
Expand All @@ -526,7 +552,7 @@ public override Task<Session> CreateSession(CreateSessionRequest request, Server
_contexts.Enqueue(context);
_headers.Enqueue(context.RequestHeaders);
var database = request.DatabaseAsDatabaseName;
return Task.FromResult(CreateSession(database));
return Task.FromResult(CreateSession(database, request.Session?.Multiplexed ?? false));
}

public override Task<Session> GetSession(GetSessionRequest request, ServerCallContext context)
Expand Down Expand Up @@ -576,7 +602,7 @@ public override Task<ExecuteBatchDmlResponse> ExecuteBatchDml(ExecuteBatchDmlReq
_executionTimes.TryGetValue(nameof(ExecuteBatchDml), out ExecutionTime executionTime);
executionTime?.SimulateExecutionTime();
_ = TryFindSession(request.SessionAsSessionName);
_ = FindOrBeginTransaction(request.SessionAsSessionName, request.Transaction);
var tx = FindOrBeginTransaction(request.SessionAsSessionName, request.Transaction);
var response = new ExecuteBatchDmlResponse
{
// TODO: Return other statuses based on the mocked results.
Expand All @@ -603,7 +629,12 @@ public override Task<ExecuteBatchDmlResponse> ExecuteBatchDml(ExecuteBatchDmlReq
{
executionTime.SimulateExecutionTime();
}
response.ResultSets.Add(CreateUpdateCountResultSet(result.UpdateCount));
var resultSet = CreateUpdateCountResultSet(result.UpdateCount);
if (index == 0 && request.Transaction?.Begin != null && tx != null)
{
resultSet.Metadata.Transaction = tx;
}
response.ResultSets.Add(resultSet);
break;
case StatementResult.StatementResultType.Exception:
if (index == 0)
Expand Down Expand Up @@ -636,7 +667,10 @@ private Status StatusFromException(Exception e)

public override async Task ExecuteStreamingSql(ExecuteSqlRequest request, IServerStreamWriter<PartialResultSet> responseStream, ServerCallContext context)
{
_requests.Enqueue(request);
if (!request.Sql.Equals(s_dialect_query))
{
_requests.Enqueue(request);
}
_contexts.Enqueue(context);
_headers.Enqueue(context.RequestHeaders);
_executionTimes.TryGetValue(nameof(ExecuteStreamingSql) + request.Sql, out ExecutionTime executionTime);
Expand Down Expand Up @@ -700,7 +734,7 @@ private async Task WriteUpdateCount(Transaction transaction, long updateCount, I
{
PartialResultSet prs = new PartialResultSet
{
Metadata = new ResultSetMetadata { Transaction = transaction },
Metadata = new ResultSetMetadata { Transaction = transaction, RowType = new StructType()},
Stats = new ResultSetStats { RowCountExact = updateCount }
};
await responseStream.WriteAsync(prs);
Expand All @@ -710,6 +744,7 @@ private ResultSet CreateUpdateCountResultSet(long updateCount)
{
ResultSet rs = new ResultSet
{
Metadata = new ResultSetMetadata { RowType = new StructType()},
Stats = new ResultSetStats { RowCountExact = updateCount }
};
return rs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Google.Cloud.Spanner.Admin.Database.V1;

namespace Google.Cloud.EntityFrameworkCore.Spanner.Tests;

Expand All @@ -38,6 +40,17 @@ public class SpannerMockServerFixture : IDisposable
public SpannerMockServerFixture()
{
SpannerMock = new MockSpannerService();
SpannerMock.AddOrUpdateStatementResult(
"select option_value from information_schema.database_options where option_name='database_dialect'",
StatementResult.CreateResultSet(
new List<Tuple<Cloud.Spanner.V1.TypeCode, string>>
{
Tuple.Create(Cloud.Spanner.V1.TypeCode.String, "option_value"),
},
new List<object[]>
{
new object[] { nameof(DatabaseDialect.GoogleStandardSql) },
}));
DatabaseAdminMock = new MockDatabaseAdminService();

var endpoint = IPEndPoint.Parse("127.0.0.1:0");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ public TypeConversionTests(SpannerMockServerFixture service)
service.SpannerMock.Reset();
}

private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port}";
private string ConnectionString => $"Data Source=projects/p1/instances/i1/databases/d1;Host={_fixture.Host};Port={_fixture.Port};UsePlainText=true";

bool UsesClientLib()
{
return Environment.GetEnvironmentVariable("USE_CLIENT_LIB") == "true";
}

[Fact]
public async Task TestEntity_ConvertValuesWithoutPrecisionLossOrOverflow_Succeeds()
Expand Down Expand Up @@ -171,7 +176,14 @@ public async Task TestEntity_ConvertValuesWithDecimalOverflow_Fails()
));

using var db = new TypeConversionDbContext(ConnectionString);
await Assert.ThrowsAsync<OverflowException>(() => db.TestEntities.FindAsync(1L).AsTask());
if (UsesClientLib())
{
await Assert.ThrowsAsync<OverflowException>(() => db.TestEntities.FindAsync(1L).AsTask());
}
else
{
await Assert.ThrowsAsync<InvalidCastException>(() => db.TestEntities.FindAsync(1L).AsTask());
}
}
}
}
Loading
Loading