diff --git a/SupabaseTests/StatelessClientTests.cs b/SupabaseTests/StatelessClientTests.cs index e882ee75..21d3f58a 100644 --- a/SupabaseTests/StatelessClientTests.cs +++ b/SupabaseTests/StatelessClientTests.cs @@ -48,16 +48,28 @@ public void GetAuthOptions_ShouldPreferDeveloperAuthorizationHeader_GivenAuthori } [TestMethod] - public void GetRestOptions_ShouldComposeSchemaAndKey() + public void GetRestOptions_ShouldComposeSchemaKeyAndAuthHeaders() { var restOptions = GetRestOptions("my-key", new Supabase.SupabaseOptions { Schema = "custom" }); using (new AssertionScope()) { restOptions.Schema.Should().Be("custom"); restOptions.Headers.Should().ContainKey("apiKey").WhoseValue.Should().Be("my-key"); + restOptions.Headers.Should().ContainKey("Authorization").WhoseValue.Should().Be("Bearer my-key", + "with no developer Authorization the bearer falls back to the supabase key"); + restOptions.Headers.Should().ContainKey("X-Client-Info"); } } + [TestMethod] + public void GetRestOptions_ShouldMergeDeveloperSuppliedHeaders_GivenCustomHeader() + { + var options = new Supabase.SupabaseOptions(); + options.Headers["X-Custom"] = "custom-value"; + GetRestOptions("my-key", options).Headers.Should().ContainKey("X-Custom") + .WhoseValue.Should().Be("custom-value", "developer headers must flow through to the child clients"); + } + [TestMethod] public void Functions_ShouldReturnClient_GivenUrlAndKey() { diff --git a/SupabaseTests/SupabaseClientCompositionTests.cs b/SupabaseTests/SupabaseClientCompositionTests.cs index de03786e..7cd26afe 100644 --- a/SupabaseTests/SupabaseClientCompositionTests.cs +++ b/SupabaseTests/SupabaseClientCompositionTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Net.WebSockets; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -200,4 +201,34 @@ public void SupabaseClient_ShouldExposeAdminAuthClient_GivenServiceKey() { UrlClient().AdminAuth("service-key").Should().NotBeNull(); } + + [TestMethod] + public void SupabaseClient_ShouldPreferSessionTokenOverApiKey_GivenActiveSession() + { + var client = UrlClient(); + var auth = Substitute.For>(); + auth.CurrentSession.Returns(new Session { AccessToken = "session-token" }); + client.Auth = auth; + client.Postgrest.GetHeaders!().Should().ContainKey("Authorization") + .WhoseValue.Should().Be("Bearer session-token", + "an active session's access token must take precedence over the api key as the bearer"); + } + + [TestMethod] + public void SupabaseClient_ShouldStopListeningToPreviousAuth_GivenAuthReplaced() + { + var previous = Substitute.For>(); + var client = DiClient(auth: previous); + client.Auth = Substitute.For>(); + previous.Received().RemoveStateChangedListener(Arg.Any.AuthEventHandler>()); + } + + [TestMethod] + public void SupabaseClient_ShouldDisconnectPreviousRealtime_GivenRealtimeReplaced() + { + var previous = RealtimeSubstitute(); + var client = DiClient(realtime: previous); + client.Realtime = Substitute.For>(); + previous.Received().Disconnect(Arg.Any(), Arg.Any()); + } } \ No newline at end of file diff --git a/SupabaseTests/SupabaseOptionsTests.cs b/SupabaseTests/SupabaseOptionsTests.cs new file mode 100644 index 00000000..28e4c7b7 --- /dev/null +++ b/SupabaseTests/SupabaseOptionsTests.cs @@ -0,0 +1,31 @@ +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Supabase; + +namespace SupabaseTests; + +/// +/// The defaults on are a public contract: they are what a client built with +/// no configuration points at (the hosted Supabase URL conventions) and how it behaves out of the box. +/// +[TestClass] +[TestCategory("Unit")] +public class SupabaseOptionsTests +{ + [TestMethod] + public void SupabaseOptions_ShouldDefaultToHostedPlatformConventions() + { + var options = new SupabaseOptions(); + using (new AssertionScope()) + { + options.Schema.Should().Be("public"); + options.AutoRefreshToken.Should().BeTrue(); + options.AuthUrlFormat.Should().Be("{0}/auth/v1"); + options.RestUrlFormat.Should().Be("{0}/rest/v1"); + options.RealtimeUrlFormat.Should().Be("{0}/realtime/v1"); + options.StorageUrlFormat.Should().Be("{0}/storage/v1"); + options.FunctionsUrlFormat.Should().Be("{0}/functions/v1"); + } + } +}