From bc95be5a7d03a6262c546caf0ca9665680ed6882 Mon Sep 17 00:00:00 2001 From: Du-z <16366766+Du-z@users.noreply.github.com> Date: Sun, 2 Oct 2022 16:12:03 +1100 Subject: [PATCH 1/3] Added tests for Scoped user signup and root signin basic auth --- src/Abstractions/Database.cs | 8 +-- src/Driver/Rest/DatabaseRest.IDatabase.cs | 4 +- src/Driver/Rest/DatabaseRest.cs | 25 +++----- src/Driver/Rest/RestClientExtensions.cs | 4 +- src/Driver/Rest/RestResponse.cs | 18 ++++-- src/Driver/Rpc/DatabaseRpc.IDatabase.cs | 4 +- src/Driver/Rpc/DatabaseRpc.cs | 18 +++--- src/Models/AuthRecords.cs | 30 +++++++++ tests/Driver.Tests/DatabaseTests.cs | 2 +- .../Driver.Tests/Queries/GeneralQueryTests.cs | 62 +++++++++++++++++++ .../Shared/{TestObject.cs => TestObjects.cs} | 15 +++++ 11 files changed, 147 insertions(+), 43 deletions(-) create mode 100644 src/Models/AuthRecords.cs rename tests/Shared/{TestObject.cs => TestObjects.cs} (65%) diff --git a/src/Abstractions/Database.cs b/src/Abstractions/Database.cs index af13a507..a96a1c2a 100644 --- a/src/Abstractions/Database.cs +++ b/src/Abstractions/Database.cs @@ -27,7 +27,7 @@ public interface IDatabase /// Signs up to a specific authentication scope. /// /// Variables used in a signin query. - public new Task Signup(object auth, CancellationToken ct = default); + public new Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : SignupRequestBase; /// /// Signs in to a specific authentication scope. @@ -36,7 +36,7 @@ public interface IDatabase /// /// This updates the internal . /// - public new Task Signin(object auth, CancellationToken ct = default); + public new Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : SigninRequestBase; /// /// Invalidates the authentication for the current connection. @@ -181,7 +181,7 @@ public interface IDatabase /// Signs up to a specific authentication scope. /// /// Variables used in a signin query. - public Task Signup(object auth, CancellationToken ct = default); + public Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : SignupRequestBase; /// /// Signs in to a specific authentication scope. @@ -190,7 +190,7 @@ public interface IDatabase /// /// This updates the internal . /// - public Task Signin(object auth, CancellationToken ct = default); + public Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : SigninRequestBase; /// /// Invalidates the authentication for the current connection. diff --git a/src/Driver/Rest/DatabaseRest.IDatabase.cs b/src/Driver/Rest/DatabaseRest.IDatabase.cs index 5b492588..240e24ae 100644 --- a/src/Driver/Rest/DatabaseRest.IDatabase.cs +++ b/src/Driver/Rest/DatabaseRest.IDatabase.cs @@ -13,11 +13,11 @@ async Task IDatabase.Use(string db, string ns, CancellationToken ct) return await Use(db, ns, ct); } - async Task IDatabase.Signup(object auth, CancellationToken ct) { + async Task IDatabase.Signup(TRequest auth, CancellationToken ct) { return await Signup(auth, ct); } - async Task IDatabase.Signin(object auth, CancellationToken ct) { + async Task IDatabase.Signin(TRequest auth, CancellationToken ct) { return await Signin(auth, ct); } diff --git a/src/Driver/Rest/DatabaseRest.cs b/src/Driver/Rest/DatabaseRest.cs index 902a5e79..d872174d 100644 --- a/src/Driver/Rest/DatabaseRest.cs +++ b/src/Driver/Rest/DatabaseRest.cs @@ -97,18 +97,18 @@ public Task Use( return CompletedOk; } - public async Task Signup( - object auth, - CancellationToken ct = default) { + public async Task Signup( + TRequest auth, + CancellationToken ct = default) where TRequest : SignupRequestBase { return await Signup(ToJsonContent(auth), ct); } - public async Task Signin( - object auth, - CancellationToken ct = default) { + public async Task Signin( + TRequest auth, + CancellationToken ct = default) where TRequest : SigninRequestBase { // SetAuth(auth.Username, auth.Password); HttpResponseMessage rsp = await _client.PostAsync("signin", ToJsonContent(auth), ct); - return await rsp.ToSurrealFromSignin(); + return await rsp.ToSurrealFromAuthResponse(); } public Task Invalidate(CancellationToken ct = default) { @@ -235,7 +235,7 @@ public async Task Signup( HttpContent auth, CancellationToken ct = default) { HttpResponseMessage rsp = await _client.PostAsync("signup", auth, ct); - return await rsp.ToSurreal(); + return await rsp.ToSurrealFromAuthResponse(); } /// @@ -329,12 +329,6 @@ private HttpContent ToJsonContent(T? v) { private static HttpContent ToContent(string s = "") { StringContent content = new(s, Encoding.UTF8, "application/json"); - - if (content.Headers.ContentType != null) { - // The server can only handle 'Content-Type' with 'application/json', remove any further information from this header - content.Headers.ContentType.CharSet = null; - } - return content; } @@ -342,9 +336,6 @@ private HttpRequestMessage ToRequestMessage( HttpMethod method, string requestUri, string content = "") { - // SurrealDb must have a 'Content-Type' header defined, - // but HttpClient does not allow default request headers to be set. - // So we need to make PUT and DELETE requests with an empty request body, but with request headers return new HttpRequestMessage { Method = method, RequestUri = new Uri(requestUri, UriKind.Relative), Content = ToContent(content), }; } } diff --git a/src/Driver/Rest/RestClientExtensions.cs b/src/Driver/Rest/RestClientExtensions.cs index d95036b6..7966dd1b 100644 --- a/src/Driver/Rest/RestClientExtensions.cs +++ b/src/Driver/Rest/RestClientExtensions.cs @@ -9,7 +9,7 @@ public static Task ToSurreal(this HttpResponseMessage msg) { } [DebuggerStepThrough] - public static Task ToSurrealFromSignin(this HttpResponseMessage msg) { - return RestResponse.FromSignin(msg); + public static Task ToSurrealFromAuthResponse(this HttpResponseMessage msg) { + return RestResponse.FromAuthResponse(msg); } } diff --git a/src/Driver/Rest/RestResponse.cs b/src/Driver/Rest/RestResponse.cs index 17b5f154..5875b266 100644 --- a/src/Driver/Rest/RestResponse.cs +++ b/src/Driver/Rest/RestResponse.cs @@ -75,14 +75,14 @@ public bool TryGetResult( /// /// Parses a signing containing JSON to a . /// - public static async Task FromSignin( + public static async Task FromAuthResponse( HttpResponseMessage msg, CancellationToken ct = default) { - // Signin returns a different object to the other response + // Signin and Signup returns a different object to the other response // And for that reason needs it's on deserialization path // The whole response is ultimately shoved into the RestResponse.Success.result field - // {"code":200,"details":"Authentication succeeded","token":""} + // {"code":200,"details":"Authentication succeeded","token":"a.jwt.token"} #if NET6_0_OR_GREATER Stream stream = await msg.Content.ReadAsStreamAsync(ct); @@ -94,8 +94,8 @@ public static async Task FromSignin( return From(err); } - JsonElement jsonElement = await JsonSerializer.DeserializeAsync(stream, SerializerOptions.Shared, ct); - Success doc = new ("", "OK", jsonElement); + AuthResult jsonElement = await JsonSerializer.DeserializeAsync(stream, SerializerOptions.Shared, ct); + Success doc = new ("", jsonElement.code.ToString(), jsonElement.token); return From(doc); } @@ -122,6 +122,7 @@ public static async Task From( } List? docs = await JsonSerializer.DeserializeAsync>(stream, SerializerOptions.Shared, ct); + Success doc = (docs?.FirstOrDefault(e => e.result.ValueKind != JsonValueKind.Null)).GetValueOrDefault(default); return From(doc); @@ -153,7 +154,7 @@ private static RestResponse From(Error? error) { private static RestResponse From(Success success) { if (success == default) { - return new(success.time, success.status, null, null, default); + return EmptyOk; } JsonElement e = success.result.IntoSingle(); return new(success.time, success.status, null, null, e); @@ -174,4 +175,9 @@ private readonly record struct Success( string time, string status, JsonElement result); + + public readonly record struct AuthResult( + HttpStatusCode code, + string details, + JsonElement token); } diff --git a/src/Driver/Rpc/DatabaseRpc.IDatabase.cs b/src/Driver/Rpc/DatabaseRpc.IDatabase.cs index 9db9fa08..d36da0b5 100644 --- a/src/Driver/Rpc/DatabaseRpc.IDatabase.cs +++ b/src/Driver/Rpc/DatabaseRpc.IDatabase.cs @@ -12,11 +12,11 @@ async Task IDatabase.Use(string db, string ns, CancellationToken ct) return await Use(db, ns, ct); } - async Task IDatabase.Signup(object auth, CancellationToken ct) { + async Task IDatabase.Signup(TRequest auth, CancellationToken ct) { return await Signup(auth, ct); } - async Task IDatabase.Signin(object auth, CancellationToken ct) { + async Task IDatabase.Signin(TRequest auth, CancellationToken ct) { return await Signin(auth, ct); } diff --git a/src/Driver/Rpc/DatabaseRpc.cs b/src/Driver/Rpc/DatabaseRpc.cs index 13821096..d3cdf0bd 100644 --- a/src/Driver/Rpc/DatabaseRpc.cs +++ b/src/Driver/Rpc/DatabaseRpc.cs @@ -74,17 +74,17 @@ public async Task Use( } /// - public async Task Signup( - object auth, - CancellationToken ct = default) { - return await _client.Send(new() { method = "signup", parameters = new() { auth, }, }, ct).ToSurreal(); + public async Task Signup( + TRequest auth, + CancellationToken ct = default) where TRequest : SignupRequestBase { + return await _client.Send(new() { method = "signup", parameters = new() { auth } }, ct).ToSurreal(); } /// - public async Task Signin( - object auth, - CancellationToken ct = default) { - WsClient.Response rsp = await _client.Send(new() { method = "signin", parameters = new() { auth, }, }, ct); + public async Task Signin( + TRequest auth, + CancellationToken ct = default) where TRequest : SigninRequestBase { + WsClient.Response rsp = await _client.Send(new() { method = "signin", parameters = new() { auth } }, ct); // TODO: Update auth return rsp.ToSurreal(); @@ -181,7 +181,7 @@ private async Task SetAuth( // TODO: Support jwt auth _config.Username = user; _config.Password = pass; - await Signin(new{ user = user, pass = pass, }, ct); + await Signin(new BasicSigninRequest(user, pass), ct); } public void Dispose() { diff --git a/src/Models/AuthRecords.cs b/src/Models/AuthRecords.cs new file mode 100644 index 00000000..d1706ade --- /dev/null +++ b/src/Models/AuthRecords.cs @@ -0,0 +1,30 @@ +using SurrealDB.Models; + +using System.Net; + +namespace SurrealDB.Models; + +/// +/// The base object for a Signup request. +/// Inherit from this object to build your own login request based on the you want to supply when signing up +/// +/// Namespace +/// Database +/// Scope +public abstract record SignupRequestBase( + string NS, + string DB, + string SC); + +/// +/// The base object for a Signin request. +/// +public abstract record SigninRequestBase(); + +/// +/// A Signin request using the "Basic" authentication scheme (RFC 7617). +/// +public record BasicSigninRequest( + string user, + string pass + ) : SigninRequestBase; diff --git a/tests/Driver.Tests/DatabaseTests.cs b/tests/Driver.Tests/DatabaseTests.cs index e5163f36..a02e8722 100644 --- a/tests/Driver.Tests/DatabaseTests.cs +++ b/tests/Driver.Tests/DatabaseTests.cs @@ -21,7 +21,7 @@ protected override async Task Run(T db) { IResponse infoResp = await db.Info(); AssertOk(infoResp); - IResponse signInStatus = await db.Signin(new{ user = TestHelper.User, pass = TestHelper.Pass, }); + IResponse signInStatus = await db.Signin(new BasicSigninRequest(TestHelper.User, TestHelper.Pass)); AssertOk(signInStatus); //AssertOk(await db.Invalidate()); diff --git a/tests/Driver.Tests/Queries/GeneralQueryTests.cs b/tests/Driver.Tests/Queries/GeneralQueryTests.cs index 559bc202..c2b7dec7 100644 --- a/tests/Driver.Tests/Queries/GeneralQueryTests.cs +++ b/tests/Driver.Tests/Queries/GeneralQueryTests.cs @@ -59,6 +59,68 @@ public async Task StopStartConnectionTest() => await DbHandle.WithDatabase( } ); + + [Fact] + public async Task SignInRootTest() => await DbHandle.WithDatabase( + async db => { + var signinObject = new BasicSigninRequest(TestHelper.User, TestHelper.Pass ); + var response = await db.Signin(signinObject); + Assert.NotNull(response); + TestHelper.AssertOk(response); + } + ); + + [Fact] + public async Task SignUpScopedUserTest() => await DbHandle.WithDatabase( + async db => { + var user = "TestUser@example.com"; + var password = "TestPassword"; + var scope = "account"; + + string sql = $"DEFINE SCOPE {scope}\n" + + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" + + " SIGNUP ( CREATE user SET email = $user, password = crypto::argon2::generate($pass) )\n" + + ";"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signupObject = new SimpleSignupRequest( user, password, TestHelper.Namespace, TestHelper.Database, scope ); + var response = await db.Signup(signupObject); + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + string? doc = result.GetObject(); + doc.Should().NotBeNullOrEmpty(); + } + ); + + [Fact] + public async Task SignUpScopedUserDefinedIdTest() => await DbHandle.WithDatabase( + async db => { + var user = "TestUser@example.com"; + var password = "TestPassword"; + var scope = "account"; + var userId = "user:123"; + + string sql = $"DEFINE SCOPE {scope}\n" + + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" + + " SIGNUP ( CREATE $id SET email = $user, password = crypto::argon2::generate($pass) )\n" + + ";"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signupObject = new IdSignupRequest( user, password, userId, TestHelper.Namespace, TestHelper.Database, scope ); + var response = await db.Signup(signupObject); + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + string? doc = result.GetObject(); + doc.Should().NotBeNullOrEmpty(); + } + ); + [Fact] public async Task SwitchDatabaseTest() => await DbHandle.WithDatabase( async db => { diff --git a/tests/Shared/TestObject.cs b/tests/Shared/TestObjects.cs similarity index 65% rename from tests/Shared/TestObject.cs rename to tests/Shared/TestObjects.cs index d8ca48d3..09347768 100644 --- a/tests/Shared/TestObject.cs +++ b/tests/Shared/TestObjects.cs @@ -21,3 +21,18 @@ public ExtendedTestObject(TKey key, TValue value, TValue mergeValue) : base (key public TValue MergeValue { get; set; } } + +public record SimpleSignupRequest( + string user, + string password, + string NS, + string DB, + string SC) : SignupRequestBase(NS, DB, SC); + +public record IdSignupRequest( + string user, + string password, + string id, + string NS, + string DB, + string SC) : SignupRequestBase(NS, DB, SC); From 88e2d582b4fd669aacfaf9f916c969b98c160178 Mon Sep 17 00:00:00 2001 From: Du-z <16366766+Du-z@users.noreply.github.com> Date: Mon, 3 Oct 2022 12:54:42 +1000 Subject: [PATCH 2/3] Added tests for Namespace and Database signin --- src/Abstractions/Database.cs | 8 +- src/Configuration/{Auth.cs => AuthMethod.cs} | 4 +- src/Configuration/Config.cs | 13 ++- src/Configuration/ConfigBuilder.cs | 4 +- src/Driver/Rest/DatabaseRest.cs | 6 +- src/Driver/Rpc/DatabaseRpc.cs | 9 +- src/Models/AuthRecords.cs | 42 ++++---- tests/Driver.Tests/DatabaseTests.cs | 2 +- .../Driver.Tests/Queries/GeneralQueryTests.cs | 99 +++++++++++++++---- tests/Shared/TestObjects.cs | 15 +-- 10 files changed, 130 insertions(+), 72 deletions(-) rename src/Configuration/{Auth.cs => AuthMethod.cs} (81%) diff --git a/src/Abstractions/Database.cs b/src/Abstractions/Database.cs index a96a1c2a..2876261c 100644 --- a/src/Abstractions/Database.cs +++ b/src/Abstractions/Database.cs @@ -27,7 +27,7 @@ public interface IDatabase /// Signs up to a specific authentication scope. /// /// Variables used in a signin query. - public new Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : SignupRequestBase; + public new Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : IAuth; /// /// Signs in to a specific authentication scope. @@ -36,7 +36,7 @@ public interface IDatabase /// /// This updates the internal . /// - public new Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : SigninRequestBase; + public new Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : IAuth; /// /// Invalidates the authentication for the current connection. @@ -181,7 +181,7 @@ public interface IDatabase /// Signs up to a specific authentication scope. /// /// Variables used in a signin query. - public Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : SignupRequestBase; + public Task Signup(TRequest auth, CancellationToken ct = default) where TRequest : IAuth; /// /// Signs in to a specific authentication scope. @@ -190,7 +190,7 @@ public interface IDatabase /// /// This updates the internal . /// - public Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : SigninRequestBase; + public Task Signin(TRequest auth, CancellationToken ct = default) where TRequest : IAuth; /// /// Invalidates the authentication for the current connection. diff --git a/src/Configuration/Auth.cs b/src/Configuration/AuthMethod.cs similarity index 81% rename from src/Configuration/Auth.cs rename to src/Configuration/AuthMethod.cs index 8c90b5e4..5df3ec51 100644 --- a/src/Configuration/Auth.cs +++ b/src/Configuration/AuthMethod.cs @@ -3,8 +3,8 @@ namespace SurrealDB.Configuration; /// /// Available authentication methods /// -public enum Auth : byte { +public enum AuthMethod : byte { None = 0, Basic, JsonWebToken, -} \ No newline at end of file +} diff --git a/src/Configuration/Config.cs b/src/Configuration/Config.cs index 626a19de..c591d13a 100644 --- a/src/Configuration/Config.cs +++ b/src/Configuration/Config.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace SurrealDB.Configuration; @@ -14,19 +14,24 @@ public struct Config { public IPEndPoint? Endpoint { get; set; } /// - /// Optional: The database to export the data from. + /// Optional: Sets the selected Database for queries. /// public string? Database { get; set; } /// - /// Optional: The namespace to export the data from. + /// Optional: Sets the selected Namespace for queries. /// public string? Namespace { get; set; } + /// + /// Optional: Sets the desired authentication scope for for a user. + /// + public string? Scope { get; set; } + /// /// The authentication method to use. /// - public Auth Authentication { get; set; } + public AuthMethod Authentication { get; set; } /// /// Database authentication username to use when connecting. diff --git a/src/Configuration/ConfigBuilder.cs b/src/Configuration/ConfigBuilder.cs index 0cda6927..5538763a 100644 --- a/src/Configuration/ConfigBuilder.cs +++ b/src/Configuration/ConfigBuilder.cs @@ -216,7 +216,7 @@ internal BasicAuth(IConfigBuilder? parent) { /// public void Configure(ref Config config) { InvalidConfigException.ThrowIfNullOrWhitespace(Username, "Username cannot be null or whitespace"); - config.Authentication = Auth.Basic; + config.Authentication = AuthMethod.Basic; config.Username = Username; config.Password = Password; } @@ -261,7 +261,7 @@ internal JwtAuth(IConfigBuilder? parent) { /// public void Configure(ref Config config) { InvalidConfigException.ThrowIfNullOrWhitespace(Token, "Invalid Json Web Token"); - config.Authentication = Auth.JsonWebToken; + config.Authentication = AuthMethod.JsonWebToken; config.JsonWebToken = Token; } diff --git a/src/Driver/Rest/DatabaseRest.cs b/src/Driver/Rest/DatabaseRest.cs index d872174d..f2d19ca1 100644 --- a/src/Driver/Rest/DatabaseRest.cs +++ b/src/Driver/Rest/DatabaseRest.cs @@ -99,14 +99,14 @@ public Task Use( public async Task Signup( TRequest auth, - CancellationToken ct = default) where TRequest : SignupRequestBase { + CancellationToken ct = default) where TRequest : IAuth { return await Signup(ToJsonContent(auth), ct); } public async Task Signin( TRequest auth, - CancellationToken ct = default) where TRequest : SigninRequestBase { - // SetAuth(auth.Username, auth.Password); + CancellationToken ct = default) where TRequest : IAuth { + HttpResponseMessage rsp = await _client.PostAsync("signin", ToJsonContent(auth), ct); return await rsp.ToSurrealFromAuthResponse(); } diff --git a/src/Driver/Rpc/DatabaseRpc.cs b/src/Driver/Rpc/DatabaseRpc.cs index d3cdf0bd..fb08290a 100644 --- a/src/Driver/Rpc/DatabaseRpc.cs +++ b/src/Driver/Rpc/DatabaseRpc.cs @@ -76,17 +76,16 @@ public async Task Use( /// public async Task Signup( TRequest auth, - CancellationToken ct = default) where TRequest : SignupRequestBase { + CancellationToken ct = default) where TRequest : IAuth { return await _client.Send(new() { method = "signup", parameters = new() { auth } }, ct).ToSurreal(); } /// public async Task Signin( TRequest auth, - CancellationToken ct = default) where TRequest : SigninRequestBase { + CancellationToken ct = default) where TRequest : IAuth { WsClient.Response rsp = await _client.Send(new() { method = "signin", parameters = new() { auth } }, ct); - - // TODO: Update auth + return rsp.ToSurreal(); } @@ -181,7 +180,7 @@ private async Task SetAuth( // TODO: Support jwt auth _config.Username = user; _config.Password = pass; - await Signin(new BasicSigninRequest(user, pass), ct); + await Signin(new RootAuth(user, pass), ct); } public void Dispose() { diff --git a/src/Models/AuthRecords.cs b/src/Models/AuthRecords.cs index d1706ade..206c411b 100644 --- a/src/Models/AuthRecords.cs +++ b/src/Models/AuthRecords.cs @@ -1,30 +1,30 @@ using SurrealDB.Models; using System.Net; +// ReSharper disable InconsistentNaming namespace SurrealDB.Models; -/// -/// The base object for a Signup request. -/// Inherit from this object to build your own login request based on the you want to supply when signing up -/// -/// Namespace -/// Database -/// Scope -public abstract record SignupRequestBase( - string NS, - string DB, - string SC); +public interface IAuth {} + +public readonly record struct RootAuth( + string user, + string pass) : IAuth; + +public readonly record struct NamespaceAuth( + string user, + string pass, + string NS) : IAuth; -/// -/// The base object for a Signin request. -/// -public abstract record SigninRequestBase(); +public readonly record struct DatabaseAuth( + string user, + string pass, + string NS, + string DB) : IAuth; -/// -/// A Signin request using the "Basic" authentication scheme (RFC 7617). -/// -public record BasicSigninRequest( +public readonly record struct ScopeAuth( string user, - string pass - ) : SigninRequestBase; + string pass, + string NS, + string DB, + string SC) : IAuth; diff --git a/tests/Driver.Tests/DatabaseTests.cs b/tests/Driver.Tests/DatabaseTests.cs index a02e8722..ddb8c3a7 100644 --- a/tests/Driver.Tests/DatabaseTests.cs +++ b/tests/Driver.Tests/DatabaseTests.cs @@ -21,7 +21,7 @@ protected override async Task Run(T db) { IResponse infoResp = await db.Info(); AssertOk(infoResp); - IResponse signInStatus = await db.Signin(new BasicSigninRequest(TestHelper.User, TestHelper.Pass)); + IResponse signInStatus = await db.Signin(new RootAuth(TestHelper.User, TestHelper.Pass)); AssertOk(signInStatus); //AssertOk(await db.Invalidate()); diff --git a/tests/Driver.Tests/Queries/GeneralQueryTests.cs b/tests/Driver.Tests/Queries/GeneralQueryTests.cs index c2b7dec7..7680216b 100644 --- a/tests/Driver.Tests/Queries/GeneralQueryTests.cs +++ b/tests/Driver.Tests/Queries/GeneralQueryTests.cs @@ -61,17 +61,62 @@ public async Task StopStartConnectionTest() => await DbHandle.WithDatabase( [Fact] - public async Task SignInRootTest() => await DbHandle.WithDatabase( + public async Task SignInRootAuthTest() => await DbHandle.WithDatabase( async db => { - var signinObject = new BasicSigninRequest(TestHelper.User, TestHelper.Pass ); + var signinObject = new RootAuth(TestHelper.User, TestHelper.Pass); var response = await db.Signin(signinObject); Assert.NotNull(response); TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().BeNullOrEmpty(); + } + ); + + [Fact] + public async Task SignInNamespaceUserTest() => await DbHandle.WithDatabase( + async db => { + var user = "DatabaseUser"; + var password = "TestPassword"; + + string sql = $"DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{password}';"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signinObject = new NamespaceAuth( user, password, TestHelper.Namespace ); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); + } + ); + + [Fact] + public async Task SignInDatabaseUserTest() => await DbHandle.WithDatabase( + async db => { + var user = "DatabaseUser"; + var password = "TestPassword"; + + string sql = $"DEFINE LOGIN {user} ON DATABASE PASSWORD '{password}';"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signinObject = new DatabaseAuth( user, password, TestHelper.Namespace, TestHelper.Database ); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); } ); [Fact] - public async Task SignUpScopedUserTest() => await DbHandle.WithDatabase( + public async Task SignUpAndSignInScopedUserTest() => await DbHandle.WithDatabase( async db => { var user = "TestUser@example.com"; var password = "TestPassword"; @@ -85,23 +130,31 @@ public async Task SignUpScopedUserTest() => await DbHandle.WithDatabase( Assert.NotNull(queryResponse); TestHelper.AssertOk(queryResponse); - var signupObject = new SimpleSignupRequest( user, password, TestHelper.Namespace, TestHelper.Database, scope ); - var response = await db.Signup(signupObject); - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - string? doc = result.GetObject(); - doc.Should().NotBeNullOrEmpty(); + var signupObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); + var signupResponse = await db.Signup(signupObject); + Assert.NotNull(signupResponse); + TestHelper.AssertOk(signupResponse); + Assert.True(signupResponse.TryGetResult(out Result signupResult)); + string? signupJwt = signupResult.GetObject(); + signupJwt.Should().NotBeNullOrEmpty(); + + var signinObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); } ); [Fact] - public async Task SignUpScopedUserDefinedIdTest() => await DbHandle.WithDatabase( + public async Task SignUpAndSignInScopedUserWithDefinedIdTest() => await DbHandle.WithDatabase( async db => { var user = "TestUser@example.com"; var password = "TestPassword"; var scope = "account"; - var userId = "user:123"; + var id = "user:123"; string sql = $"DEFINE SCOPE {scope}\n" + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" @@ -111,13 +164,21 @@ public async Task SignUpScopedUserDefinedIdTest() => await DbHandle.WithDatab Assert.NotNull(queryResponse); TestHelper.AssertOk(queryResponse); - var signupObject = new IdSignupRequest( user, password, userId, TestHelper.Namespace, TestHelper.Database, scope ); - var response = await db.Signup(signupObject); - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - string? doc = result.GetObject(); - doc.Should().NotBeNullOrEmpty(); + var signupObject = new IdScopeAuth( id, user, password, TestHelper.Namespace, TestHelper.Database, scope ); + var signupResponse = await db.Signup(signupObject); + Assert.NotNull(signupResponse); + TestHelper.AssertOk(signupResponse); + Assert.True(signupResponse.TryGetResult(out Result signupResult)); + string? signupJwt = signupResult.GetObject(); + signupJwt.Should().NotBeNullOrEmpty(); + + var signinObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); } ); diff --git a/tests/Shared/TestObjects.cs b/tests/Shared/TestObjects.cs index 09347768..ffe656bd 100644 --- a/tests/Shared/TestObjects.cs +++ b/tests/Shared/TestObjects.cs @@ -22,17 +22,10 @@ public ExtendedTestObject(TKey key, TValue value, TValue mergeValue) : base (key public TValue MergeValue { get; set; } } -public record SimpleSignupRequest( - string user, - string password, - string NS, - string DB, - string SC) : SignupRequestBase(NS, DB, SC); - -public record IdSignupRequest( - string user, - string password, +public record IdScopeAuth( string id, + string user, + string pass, string NS, string DB, - string SC) : SignupRequestBase(NS, DB, SC); + string SC) : IAuth; From 9fb4ad7dce314db8002e2c7ec5039ac262a89f97 Mon Sep 17 00:00:00 2001 From: Du-z <16366766+Du-z@users.noreply.github.com> Date: Mon, 3 Oct 2022 15:50:07 +1000 Subject: [PATCH 3/3] Auth tests now retrieves $token and $auth information Split GeneralQueryTests --- src/Driver/Rest/DatabaseRest.cs | 18 +- src/Models/AuthRecords.cs | 10 + tests/Driver.Tests/Queries/AuthQueryTests.cs | 236 +++++++++++++++++ .../Driver.Tests/Queries/GeneralQueryTests.cs | 240 +----------------- .../Queries/ManagementQueryTests.cs | 138 ++++++++++ tests/Shared/TestObjects.cs | 6 +- 6 files changed, 406 insertions(+), 242 deletions(-) create mode 100644 tests/Driver.Tests/Queries/AuthQueryTests.cs create mode 100644 tests/Driver.Tests/Queries/ManagementQueryTests.cs diff --git a/src/Driver/Rest/DatabaseRest.cs b/src/Driver/Rest/DatabaseRest.cs index f2d19ca1..595b8f87 100644 --- a/src/Driver/Rest/DatabaseRest.cs +++ b/src/Driver/Rest/DatabaseRest.cs @@ -121,7 +121,9 @@ public Task Invalidate(CancellationToken ct = default) { public Task Authenticate( string token, CancellationToken ct = default) { - throw new NotSupportedException(); // TODO: Is it tho??? + SetAuth(token); + + return CompletedOk; } public Task Let( @@ -197,7 +199,8 @@ private void ConfigureClients() { private void SetAuth( string? user, string? pass) { - // TODO: Support jwt auth + RemoveAuth(); + _config.Username = user; _config.Password = pass; AuthenticationHeaderValue header = new( @@ -208,7 +211,18 @@ private void SetAuth( _client.DefaultRequestHeaders.Authorization = header; } + private void SetAuth( + string? jwt) { + RemoveAuth(); + + _config.JsonWebToken = jwt; + AuthenticationHeaderValue header = new("Bearer", jwt); + + _client.DefaultRequestHeaders.Authorization = header; + } + private void RemoveAuth() { + _config.JsonWebToken = null; _config.Username = null; _config.Password = null; _client.DefaultRequestHeaders.Authorization = null; diff --git a/src/Models/AuthRecords.cs b/src/Models/AuthRecords.cs index 206c411b..d9060ff2 100644 --- a/src/Models/AuthRecords.cs +++ b/src/Models/AuthRecords.cs @@ -28,3 +28,13 @@ public readonly record struct ScopeAuth( string NS, string DB, string SC) : IAuth; + +public readonly record struct Token( + string ID, + string NS, + string DB, + string SC, + DateTime exp, + DateTime iat, + DateTime nbf, + string iss) : IAuth; diff --git a/tests/Driver.Tests/Queries/AuthQueryTests.cs b/tests/Driver.Tests/Queries/AuthQueryTests.cs new file mode 100644 index 00000000..d91278df --- /dev/null +++ b/tests/Driver.Tests/Queries/AuthQueryTests.cs @@ -0,0 +1,236 @@ +using SurrealDB.Common; +// ReSharper disable All +#pragma warning disable CS0169 + +namespace SurrealDB.Driver.Tests.Queries; +public class RestAuthQueryTests : AuthQueryTests { + public RestAuthQueryTests(ITestOutputHelper logger) : base(logger) { + } +} +public class RpcAuthQueryTests : AuthQueryTests { + public RpcAuthQueryTests(ITestOutputHelper logger) : base(logger) { + } +} + +[Collection("SurrealDBRequired")] +public abstract class AuthQueryTests + where T : IDatabase, IDisposable, new() { + + protected readonly ITestOutputHelper Logger; + + public AuthQueryTests(ITestOutputHelper logger) { + Logger = logger; + } + + [Fact] + public async Task SignInRootAuthTest() => await DbHandle.WithDatabase( + async db => { + var signinObject = new RootAuth(TestHelper.User, TestHelper.Pass); + var response = await db.Signin(signinObject); + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().BeNullOrEmpty(); + } + ); + + [Fact] + public async Task SignInNamespaceUserTest() => await DbHandle.WithDatabase( + async db => { + var user = "DatabaseUser"; + var password = "TestPassword"; + + string sql = $"DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{password}';"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signinObject = new NamespaceAuth(user, password, TestHelper.Namespace); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); + + var authenticateResponse = await db.Authenticate(signinJwt); + Assert.NotNull(authenticateResponse); + TestHelper.AssertOk(authenticateResponse); + + string tokenSql = $"SELECT * FROM $token;"; + var tokenQueryResponse = await db.Query(tokenSql, null); + Assert.NotNull(tokenQueryResponse); + TestHelper.AssertOk(tokenQueryResponse); + Assert.True(tokenQueryResponse.TryGetResult(out Result tokenQueryResult)); + Token? token = tokenQueryResult.GetObject(); + token.Should().NotBeNull(); + token.Value.ID.Should().Be(user); + token.Value.NS.Should().Be(TestHelper.Namespace); + } + ); + + [Fact] + public async Task SignInDatabaseUserTest() => await DbHandle.WithDatabase( + async db => { + var user = "DatabaseUser"; + var password = "TestPassword"; + + string sql = $"DEFINE LOGIN {user} ON DATABASE PASSWORD '{password}';"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signinObject = new DatabaseAuth(user, password, TestHelper.Namespace, TestHelper.Database); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); + + var authenticateResponse = await db.Authenticate(signinJwt); + Assert.NotNull(authenticateResponse); + TestHelper.AssertOk(authenticateResponse); + + string tokenSql = $"SELECT * FROM $token;"; + var tokenQueryResponse = await db.Query(tokenSql, null); + Assert.NotNull(tokenQueryResponse); + TestHelper.AssertOk(tokenQueryResponse); + Assert.True(tokenQueryResponse.TryGetResult(out Result tokenQueryResult)); + Token? token = tokenQueryResult.GetObject(); + token.Should().NotBeNull(); + token.Value.ID.Should().Be(user); + token.Value.NS.Should().Be(TestHelper.Namespace); + token.Value.DB.Should().Be(TestHelper.Database); + } + ); + + [Fact] + public async Task SignUpAndSignInScopedUserTest() => await DbHandle.WithDatabase( + async db => { + var email = "TestUser@example.com"; + var password = "TestPassword"; + var scope = "account"; + + string sql = $"DEFINE SCOPE {scope}\n" + + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" + + " SIGNUP ( CREATE user SET email = $user, password = crypto::argon2::generate($pass) )\n" + + ";" + + "" + + "DEFINE TABLE user SCHEMALESS\n" + + " PERMISSIONS\n" + + " FOR select, update WHERE id = $auth.id,\n FOR create, delete NONE;" + + ";"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signupObject = new ScopeAuth(email, password, TestHelper.Namespace, TestHelper.Database, scope); + var signupResponse = await db.Signup(signupObject); + Assert.NotNull(signupResponse); + TestHelper.AssertOk(signupResponse); + Assert.True(signupResponse.TryGetResult(out Result signupResult)); + string? signupJwt = signupResult.GetObject(); + signupJwt.Should().NotBeNullOrEmpty(); + + var signinObject = new ScopeAuth(email, password, TestHelper.Namespace, TestHelper.Database, scope); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); + + var authenticateResponse = await db.Authenticate(signinJwt); + Assert.NotNull(authenticateResponse); + TestHelper.AssertOk(authenticateResponse); + + string tokenSql = $"SELECT * FROM $token;"; + var tokenQueryResponse = await db.Query(tokenSql, null); + Assert.NotNull(tokenQueryResponse); + TestHelper.AssertOk(tokenQueryResponse); + Assert.True(tokenQueryResponse.TryGetResult(out Result tokenQueryResult)); + Token? token = tokenQueryResult.GetObject(); + token.Should().NotBeNull(); + token.Value.NS.Should().Be(TestHelper.Namespace); + token.Value.DB.Should().Be(TestHelper.Database); + token.Value.SC.Should().Be(scope); + + string authSql = $"SELECT id, email FROM $auth;"; // This query required permisions to be set on the user table + var authQueryResponse = await db.Query(authSql, null); + Assert.NotNull(authQueryResponse); + TestHelper.AssertOk(authQueryResponse); + Assert.True(authQueryResponse.TryGetResult(out Result authQueryResult)); + User? user = authQueryResult.GetObject(); + user.Should().NotBeNull(); + user.Value.id.Should().Be(token.Value.ID); + user.Value.email.Should().Be(email); + } + ); + + [Fact] + public async Task SignUpAndSignInScopedUserWithDefinedIdTest() => await DbHandle.WithDatabase( + async db => { + var email = "TestUser@example.com"; + var password = "TestPassword"; + var scope = "account"; + var id = "user:123"; + + string sql = $"DEFINE SCOPE {scope}\n" + + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" + + " SIGNUP ( CREATE $id SET email = $user, password = crypto::argon2::generate($pass) )\n" + + ";" + + "" + + "DEFINE TABLE user SCHEMALESS\n" + + " PERMISSIONS\n" + + " FOR select, update WHERE id = $auth.id,\n FOR create, delete NONE;" + + ";"; + var queryResponse = await db.Query(sql, null); + Assert.NotNull(queryResponse); + TestHelper.AssertOk(queryResponse); + + var signupObject = new IdScopeAuth(id, email, password, TestHelper.Namespace, TestHelper.Database, scope); + var signupResponse = await db.Signup(signupObject); + Assert.NotNull(signupResponse); + TestHelper.AssertOk(signupResponse); + Assert.True(signupResponse.TryGetResult(out Result signupResult)); + string? signupJwt = signupResult.GetObject(); + signupJwt.Should().NotBeNullOrEmpty(); + + var signinObject = new ScopeAuth(email, password, TestHelper.Namespace, TestHelper.Database, scope); + var signinResponse = await db.Signin(signinObject); + Assert.NotNull(signinResponse); + TestHelper.AssertOk(signinResponse); + Assert.True(signinResponse.TryGetResult(out Result result)); + string? signinJwt = result.GetObject(); + signinJwt.Should().NotBeNullOrEmpty(); + + var authenticateResponse = await db.Authenticate(signinJwt); + Assert.NotNull(authenticateResponse); + TestHelper.AssertOk(authenticateResponse); + + string tokenSql = $"SELECT * FROM $token;"; + var tokenQueryResponse = await db.Query(tokenSql, null); + Assert.NotNull(tokenQueryResponse); + TestHelper.AssertOk(tokenQueryResponse); + Assert.True(tokenQueryResponse.TryGetResult(out Result tokenQueryResult)); + Token? token = tokenQueryResult.GetObject(); + token.Should().NotBeNull(); + token.Value.ID.Should().Be(id); + token.Value.NS.Should().Be(TestHelper.Namespace); + token.Value.DB.Should().Be(TestHelper.Database); + token.Value.SC.Should().Be(scope); + + string authSql = $"SELECT id, email FROM $auth;"; // This query required permisions to be set on the user table + var authQueryResponse = await db.Query(authSql, null); + Assert.NotNull(authQueryResponse); + TestHelper.AssertOk(authQueryResponse); + Assert.True(authQueryResponse.TryGetResult(out Result authQueryResult)); + User? user = authQueryResult.GetObject(); + user.Should().NotBeNull(); + user.Value.id.Should().Be(token.Value.ID); + user.Value.email.Should().Be(email); + } + ); +} diff --git a/tests/Driver.Tests/Queries/GeneralQueryTests.cs b/tests/Driver.Tests/Queries/GeneralQueryTests.cs index 7680216b..1608af8f 100644 --- a/tests/Driver.Tests/Queries/GeneralQueryTests.cs +++ b/tests/Driver.Tests/Queries/GeneralQueryTests.cs @@ -21,8 +21,7 @@ public abstract class GeneralQueryTests public GeneralQueryTests(ITestOutputHelper logger) { Logger = logger; } - - + private record GroupedCountries { string? country; string? total; @@ -37,243 +36,6 @@ private class MathResultDocument { public float result {get; set;} } - [Fact] - public async Task StopStartConnectionTest() => await DbHandle.WithDatabase( - async db => { - string sql = "INFO FOR DB;"; - var response = await db.Query(sql, null); - Assert.NotNull(response); - TestHelper.AssertOk(response); - - await db.Close(); - await Assert.ThrowsAsync(async () => await db.Query(sql, null)); - db.Dispose(); - await Assert.ThrowsAsync(async () => await db.Query(sql, null)); - - db = new(); - await db.Open(TestHelper.Default); - - response = await db.Query(sql, null); - Assert.NotNull(response); - TestHelper.AssertOk(response); - } - ); - - - [Fact] - public async Task SignInRootAuthTest() => await DbHandle.WithDatabase( - async db => { - var signinObject = new RootAuth(TestHelper.User, TestHelper.Pass); - var response = await db.Signin(signinObject); - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - string? signinJwt = result.GetObject(); - signinJwt.Should().BeNullOrEmpty(); - } - ); - - [Fact] - public async Task SignInNamespaceUserTest() => await DbHandle.WithDatabase( - async db => { - var user = "DatabaseUser"; - var password = "TestPassword"; - - string sql = $"DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{password}';"; - var queryResponse = await db.Query(sql, null); - Assert.NotNull(queryResponse); - TestHelper.AssertOk(queryResponse); - - var signinObject = new NamespaceAuth( user, password, TestHelper.Namespace ); - var signinResponse = await db.Signin(signinObject); - Assert.NotNull(signinResponse); - TestHelper.AssertOk(signinResponse); - Assert.True(signinResponse.TryGetResult(out Result result)); - string? signinJwt = result.GetObject(); - signinJwt.Should().NotBeNullOrEmpty(); - } - ); - - [Fact] - public async Task SignInDatabaseUserTest() => await DbHandle.WithDatabase( - async db => { - var user = "DatabaseUser"; - var password = "TestPassword"; - - string sql = $"DEFINE LOGIN {user} ON DATABASE PASSWORD '{password}';"; - var queryResponse = await db.Query(sql, null); - Assert.NotNull(queryResponse); - TestHelper.AssertOk(queryResponse); - - var signinObject = new DatabaseAuth( user, password, TestHelper.Namespace, TestHelper.Database ); - var signinResponse = await db.Signin(signinObject); - Assert.NotNull(signinResponse); - TestHelper.AssertOk(signinResponse); - Assert.True(signinResponse.TryGetResult(out Result result)); - string? signinJwt = result.GetObject(); - signinJwt.Should().NotBeNullOrEmpty(); - } - ); - - [Fact] - public async Task SignUpAndSignInScopedUserTest() => await DbHandle.WithDatabase( - async db => { - var user = "TestUser@example.com"; - var password = "TestPassword"; - var scope = "account"; - - string sql = $"DEFINE SCOPE {scope}\n" - + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" - + " SIGNUP ( CREATE user SET email = $user, password = crypto::argon2::generate($pass) )\n" - + ";"; - var queryResponse = await db.Query(sql, null); - Assert.NotNull(queryResponse); - TestHelper.AssertOk(queryResponse); - - var signupObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); - var signupResponse = await db.Signup(signupObject); - Assert.NotNull(signupResponse); - TestHelper.AssertOk(signupResponse); - Assert.True(signupResponse.TryGetResult(out Result signupResult)); - string? signupJwt = signupResult.GetObject(); - signupJwt.Should().NotBeNullOrEmpty(); - - var signinObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); - var signinResponse = await db.Signin(signinObject); - Assert.NotNull(signinResponse); - TestHelper.AssertOk(signinResponse); - Assert.True(signinResponse.TryGetResult(out Result result)); - string? signinJwt = result.GetObject(); - signinJwt.Should().NotBeNullOrEmpty(); - } - ); - - [Fact] - public async Task SignUpAndSignInScopedUserWithDefinedIdTest() => await DbHandle.WithDatabase( - async db => { - var user = "TestUser@example.com"; - var password = "TestPassword"; - var scope = "account"; - var id = "user:123"; - - string sql = $"DEFINE SCOPE {scope}\n" - + " SIGNIN ( SELECT * FROM user WHERE email = $user AND crypto::argon2::compare(password, $pass) )\n" - + " SIGNUP ( CREATE $id SET email = $user, password = crypto::argon2::generate($pass) )\n" - + ";"; - var queryResponse = await db.Query(sql, null); - Assert.NotNull(queryResponse); - TestHelper.AssertOk(queryResponse); - - var signupObject = new IdScopeAuth( id, user, password, TestHelper.Namespace, TestHelper.Database, scope ); - var signupResponse = await db.Signup(signupObject); - Assert.NotNull(signupResponse); - TestHelper.AssertOk(signupResponse); - Assert.True(signupResponse.TryGetResult(out Result signupResult)); - string? signupJwt = signupResult.GetObject(); - signupJwt.Should().NotBeNullOrEmpty(); - - var signinObject = new ScopeAuth( user, password, TestHelper.Namespace, TestHelper.Database, scope ); - var signinResponse = await db.Signin(signinObject); - Assert.NotNull(signinResponse); - TestHelper.AssertOk(signinResponse); - Assert.True(signinResponse.TryGetResult(out Result result)); - string? signinJwt = result.GetObject(); - signinJwt.Should().NotBeNullOrEmpty(); - } - ); - - [Fact] - public async Task SwitchDatabaseTest() => await DbHandle.WithDatabase( - async db => { - var nsName = db.GetConfig().Namespace!; - var originalDbName = db.GetConfig().Database!; - var otherDbName = "DifferentDb"; - - TestObject expectedOriginalObject = new(1, originalDbName); - TestObject expectedOtherObject = new(1, otherDbName); - - Thing thing = Thing.From("object", expectedOriginalObject.Key.ToString()); - await db.Create(thing, expectedOriginalObject); - - { - var useResponse = await db.Use(otherDbName, nsName); - Assert.NotNull(useResponse); - TestHelper.AssertOk(useResponse); - - await db.Create(thing, expectedOtherObject); - - var response = await db.Select(thing); - - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - TestObject? doc = result.GetObject>(); - doc.Should().BeEquivalentTo(expectedOtherObject); - } - - { - var useResponse = await db.Use(originalDbName, nsName); - Assert.NotNull(useResponse); - TestHelper.AssertOk(useResponse); - - var response = await db.Select(thing); - - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - TestObject? doc = result.GetObject>(); - doc.Should().BeEquivalentTo(expectedOriginalObject); - } - - } - ); - - [Fact] - public async Task SwitchNamespaceTest() => await DbHandle.WithDatabase( - async db => { - var originalNsName = db.GetConfig().Namespace!; - var otherNsName = "DifferentNs"; - var dbName = db.GetConfig().Database!; - - TestObject expectedOriginalObject = new(1, originalNsName); - TestObject expectedOtherObject = new(1, otherNsName); - - Thing thing = Thing.From("object", expectedOriginalObject.Key.ToString()); - await db.Create(thing, expectedOriginalObject); - - { - var useResponse = await db.Use(dbName, otherNsName); - Assert.NotNull(useResponse); - TestHelper.AssertOk(useResponse); - - await db.Create(thing, expectedOtherObject); - - var response = await db.Select(thing); - - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - TestObject? doc = result.GetObject>(); - doc.Should().BeEquivalentTo(expectedOtherObject); - } - - { - var useResponse = await db.Use(dbName, originalNsName); - Assert.NotNull(useResponse); - TestHelper.AssertOk(useResponse); - - var response = await db.Select(thing); - - Assert.NotNull(response); - TestHelper.AssertOk(response); - Assert.True(response.TryGetResult(out Result result)); - TestObject? doc = result.GetObject>(); - doc.Should().BeEquivalentTo(expectedOriginalObject); - } - - } - ); - [Fact] public async Task SimpleFuturesQueryTest() => await DbHandle.WithDatabase( async db => { diff --git a/tests/Driver.Tests/Queries/ManagementQueryTests.cs b/tests/Driver.Tests/Queries/ManagementQueryTests.cs new file mode 100644 index 00000000..12b4c272 --- /dev/null +++ b/tests/Driver.Tests/Queries/ManagementQueryTests.cs @@ -0,0 +1,138 @@ +using SurrealDB.Common; +// ReSharper disable All +#pragma warning disable CS0169 + +namespace SurrealDB.Driver.Tests.Queries; +public class RestManagementQueryTests : ManagementQueryTests { + public RestManagementQueryTests(ITestOutputHelper logger) : base(logger) { + } +} +public class RpcManagementQueryTests : ManagementQueryTests { + public RpcManagementQueryTests(ITestOutputHelper logger) : base(logger) { + } +} + +[Collection("SurrealDBRequired")] +public abstract class ManagementQueryTests + where T : IDatabase, IDisposable, new() { + + protected readonly ITestOutputHelper Logger; + + public ManagementQueryTests(ITestOutputHelper logger) { + Logger = logger; + } + + [Fact] + public async Task StopStartConnectionTest() => await DbHandle.WithDatabase( + async db => { + string sql = "INFO FOR DB;"; + var response = await db.Query(sql, null); + Assert.NotNull(response); + TestHelper.AssertOk(response); + + await db.Close(); + await Assert.ThrowsAsync(async () => await db.Query(sql, null)); + db.Dispose(); + await Assert.ThrowsAsync(async () => await db.Query(sql, null)); + + db = new(); + await db.Open(TestHelper.Default); + + response = await db.Query(sql, null); + Assert.NotNull(response); + TestHelper.AssertOk(response); + } + ); + + [Fact] + public async Task SwitchDatabaseTest() => await DbHandle.WithDatabase( + async db => { + var nsName = db.GetConfig().Namespace!; + var originalDbName = db.GetConfig().Database!; + var otherDbName = "DifferentDb"; + + TestObject expectedOriginalObject = new(1, originalDbName); + TestObject expectedOtherObject = new(1, otherDbName); + + Thing thing = Thing.From("object", expectedOriginalObject.Key.ToString()); + await db.Create(thing, expectedOriginalObject); + + { + var useResponse = await db.Use(otherDbName, nsName); + Assert.NotNull(useResponse); + TestHelper.AssertOk(useResponse); + + await db.Create(thing, expectedOtherObject); + + var response = await db.Select(thing); + + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + TestObject? doc = result.GetObject>(); + doc.Should().BeEquivalentTo(expectedOtherObject); + } + + { + var useResponse = await db.Use(originalDbName, nsName); + Assert.NotNull(useResponse); + TestHelper.AssertOk(useResponse); + + var response = await db.Select(thing); + + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + TestObject? doc = result.GetObject>(); + doc.Should().BeEquivalentTo(expectedOriginalObject); + } + + } + ); + + [Fact] + public async Task SwitchNamespaceTest() => await DbHandle.WithDatabase( + async db => { + var originalNsName = db.GetConfig().Namespace!; + var otherNsName = "DifferentNs"; + var dbName = db.GetConfig().Database!; + + TestObject expectedOriginalObject = new(1, originalNsName); + TestObject expectedOtherObject = new(1, otherNsName); + + Thing thing = Thing.From("object", expectedOriginalObject.Key.ToString()); + await db.Create(thing, expectedOriginalObject); + + { + var useResponse = await db.Use(dbName, otherNsName); + Assert.NotNull(useResponse); + TestHelper.AssertOk(useResponse); + + await db.Create(thing, expectedOtherObject); + + var response = await db.Select(thing); + + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + TestObject? doc = result.GetObject>(); + doc.Should().BeEquivalentTo(expectedOtherObject); + } + + { + var useResponse = await db.Use(dbName, originalNsName); + Assert.NotNull(useResponse); + TestHelper.AssertOk(useResponse); + + var response = await db.Select(thing); + + Assert.NotNull(response); + TestHelper.AssertOk(response); + Assert.True(response.TryGetResult(out Result result)); + TestObject? doc = result.GetObject>(); + doc.Should().BeEquivalentTo(expectedOriginalObject); + } + + } + ); +} diff --git a/tests/Shared/TestObjects.cs b/tests/Shared/TestObjects.cs index ffe656bd..d545c957 100644 --- a/tests/Shared/TestObjects.cs +++ b/tests/Shared/TestObjects.cs @@ -22,10 +22,14 @@ public ExtendedTestObject(TKey key, TValue value, TValue mergeValue) : base (key public TValue MergeValue { get; set; } } -public record IdScopeAuth( +public record struct IdScopeAuth( string id, string user, string pass, string NS, string DB, string SC) : IAuth; + +public record struct User( + string id, + string email);