diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a36e6791..09c973f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,115 +14,109 @@ concurrency: cancel-in-progress: true jobs: - xcodebuild-latest: - name: xcodebuild (16) - runs-on: macos-15 - strategy: - matrix: - command: [test, ""] - platform: [IOS, MACOS] - xcode: ["16.2"] - include: - - { command: test, skip_release: 1 } - steps: - - uses: actions/checkout@v4 - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - name: List available devices - run: xcrun simctl list devices available - - name: Set IgnoreFileSystemDeviceInodeChanges flag - run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES - - name: Update mtime for incremental builds - uses: chetan/git-restore-mtime-action@v2 - - name: Debug - run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" xcodebuild - - name: Release - if: matrix.skip_release != '1' - run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Release PLATFORM="${{ matrix.platform }}" xcodebuild - - name: Instal lcov - if: matrix.command == 'test' && matrix.platform == 'IOS' - run: brew install lcov - - name: Export code coverage - id: coverage - if: matrix.command == 'test' && matrix.platform == 'IOS' - run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" coverage - - uses: coverallsapp/github-action@v2.3.4 - if: steps.coverage.outcome == 'success' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - file: lcov.info - - xcodebuild: - name: xcodebuild (15) - runs-on: macos-14 - strategy: - matrix: - command: [test, ""] - platform: [IOS, MAC_CATALYST, MACOS, TVOS, VISIONOS, WATCHOS] - xcode: [15.2, 15.4] - exclude: - - { xcode: 15.2, command: test } - - { xcode: 15.2, platform: MAC_CATALYST } - - { xcode: 15.2, platform: TVOS } - - { xcode: 15.2, platform: VISIONOS } - - { xcode: 15.2, platform: WATCHOS } - - { command: test, platform: WATCHOS } - include: - - { command: test, skip_release: 1 } - steps: - - uses: actions/checkout@v4 - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - name: Install visionOS runtime - if: matrix.platform == 'visionOS' - run: | - sudo xcodebuild -runFirstLaunch - sudo xcrun simctl list - sudo xcodebuild -downloadPlatform visionOS - sudo xcodebuild -runFirstLaunch - - name: List available devices - run: xcrun simctl list devices available - - name: Cache derived data - uses: actions/cache@v3 - with: - path: | - ~/.derivedData - key: | - deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift') }} - restore-keys: | - deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}- - - name: Set IgnoreFileSystemDeviceInodeChanges flag - run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES - - name: Update mtime for incremental builds - uses: chetan/git-restore-mtime-action@v2 - - name: Debug - run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" xcodebuild - - name: Release - if: matrix.skip_release != '1' - run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Release PLATFORM="${{ matrix.platform }}" xcodebuild - - # linux: - # name: linux + # xcodebuild-latest: + # name: xcodebuild (16) + # runs-on: macos-15 # strategy: # matrix: - # swift-version: ["5.10"] - # runs-on: ubuntu-latest + # command: [test, ""] + # platform: [IOS, MACOS] + # xcode: ["16.2"] + # include: + # - { command: test, skip_release: 1 } # steps: # - uses: actions/checkout@v4 - # - uses: swift-actions/setup-swift@v2 + # - name: Select Xcode ${{ matrix.xcode }} + # run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app + # - name: List available devices + # run: xcrun simctl list devices available + # - name: Set IgnoreFileSystemDeviceInodeChanges flag + # run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES + # - name: Update mtime for incremental builds + # uses: chetan/git-restore-mtime-action@v2 + # - name: Debug + # run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" xcodebuild + # - name: Release + # if: matrix.skip_release != '1' + # run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Release PLATFORM="${{ matrix.platform }}" xcodebuild + # - name: Instal lcov + # if: matrix.command == 'test' && matrix.platform == 'IOS' + # run: brew install lcov + # - name: Export code coverage + # id: coverage + # if: matrix.command == 'test' && matrix.platform == 'IOS' + # run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" coverage + # - uses: coverallsapp/github-action@v2.3.4 + # if: steps.coverage.outcome == 'success' # with: - # swift-version: ${{ matrix.swift-version }} - # - name: Cache build + # github-token: ${{ secrets.GITHUB_TOKEN }} + # file: lcov.info + + # xcodebuild: + # name: xcodebuild (15) + # runs-on: macos-14 + # strategy: + # matrix: + # command: [test, ""] + # platform: [IOS, MAC_CATALYST, MACOS, TVOS, VISIONOS, WATCHOS] + # xcode: [15.2, 15.4] + # exclude: + # - { xcode: 15.2, command: test } + # - { xcode: 15.2, platform: MAC_CATALYST } + # - { xcode: 15.2, platform: TVOS } + # - { xcode: 15.2, platform: VISIONOS } + # - { xcode: 15.2, platform: WATCHOS } + # - { command: test, platform: WATCHOS } + # include: + # - { command: test, skip_release: 1 } + # steps: + # - uses: actions/checkout@v4 + # - name: Select Xcode ${{ matrix.xcode }} + # run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app + # - name: Install visionOS runtime + # if: matrix.platform == 'visionOS' + # run: | + # sudo xcodebuild -runFirstLaunch + # sudo xcrun simctl list + # sudo xcodebuild -downloadPlatform visionOS + # sudo xcodebuild -runFirstLaunch + # - name: List available devices + # run: xcrun simctl list devices available + # - name: Cache derived data # uses: actions/cache@v3 # with: # path: | - # .build + # ~/.derivedData # key: | - # build-spm-linux-${{ matrix.swift-version }}-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift', '**/Package.resolved') }} + # deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift') }} # restore-keys: | - # build-spm-linux-${{ matrix.swift-version }}- - # - name: Run tests - # run: swift test --skip IntegrationTests + # deriveddata-xcodebuild-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command }}- + # - name: Set IgnoreFileSystemDeviceInodeChanges flag + # run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES + # - name: Update mtime for incremental builds + # uses: chetan/git-restore-mtime-action@v2 + # - name: Debug + # run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Debug PLATFORM="${{ matrix.platform }}" xcodebuild + # - name: Release + # if: matrix.skip_release != '1' + # run: make XCODEBUILD_ARGUMENT="${{ matrix.command }}" CONFIG=Release PLATFORM="${{ matrix.platform }}" xcodebuild + + integration-tests: + name: integration tests (linux) + strategy: + matrix: + swift-version: ["6"] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ matrix.swift-version }} + - uses: supabase/setup-cli@v1 + with: + version: latest + - name: Run integration tests + run: make test-integration # library-evolution: # name: Library (evolution) @@ -134,28 +128,28 @@ jobs: # - name: Build for library evolution # run: make build-for-library-evolution - examples: - name: Examples - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - - name: Cache derived data - uses: actions/cache@v3 - with: - path: ~/.derivedData - key: | - deriveddata-examples-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift', '**/Examples/**/*.swift') }} - restore-keys: | - deriveddata-examples- - - name: Select Xcode 16 - run: sudo xcode-select -s /Applications/Xcode_16.0.app - - name: Set IgnoreFileSystemDeviceInodeChanges flag - run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES - - name: Update mtime for incremental builds - uses: chetan/git-restore-mtime-action@v2 - - name: Examples - run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Examples" XCODEBUILD_ARGUMENT=build xcodebuild - - name: SlackClone - run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SlackClone" XCODEBUILD_ARGUMENT=build xcodebuild - - name: UserManagement - run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="UserManagement" XCODEBUILD_ARGUMENT=build xcodebuild + # examples: + # name: Examples + # runs-on: macos-15 + # steps: + # - uses: actions/checkout@v4 + # - name: Cache derived data + # uses: actions/cache@v3 + # with: + # path: ~/.derivedData + # key: | + # deriveddata-examples-${{ hashFiles('**/Sources/**/*.swift', '**/Tests/**/*.swift', '**/Examples/**/*.swift') }} + # restore-keys: | + # deriveddata-examples- + # - name: Select Xcode 16 + # run: sudo xcode-select -s /Applications/Xcode_16.0.app + # - name: Set IgnoreFileSystemDeviceInodeChanges flag + # run: defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES + # - name: Update mtime for incremental builds + # uses: chetan/git-restore-mtime-action@v2 + # - name: Examples + # run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="Examples" XCODEBUILD_ARGUMENT=build xcodebuild + # - name: SlackClone + # run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="SlackClone" XCODEBUILD_ARGUMENT=build xcodebuild + # - name: UserManagement + # run: make DERIVED_DATA_PATH=~/.derivedData SCHEME="UserManagement" XCODEBUILD_ARGUMENT=build xcodebuild diff --git a/Sources/TestHelpers/MockExtensions.swift b/Sources/TestHelpers/MockExtensions.swift index 8b585d31..e7c8fe14 100644 --- a/Sources/TestHelpers/MockExtensions.swift +++ b/Sources/TestHelpers/MockExtensions.swift @@ -12,7 +12,7 @@ import InlineSnapshotTesting extension Mock { package func snapshotRequest( message: @autoclosure () -> String = "", - record isRecording: Bool? = nil, + record: SnapshotTestingConfiguration.Record? = nil, timeout: TimeInterval = 5, syntaxDescriptor: InlineSnapshotSyntaxDescriptor = InlineSnapshotSyntaxDescriptor(), matches expected: (() -> String)? = nil, @@ -27,7 +27,7 @@ extension Mock { assertInlineSnapshot( of: $0, as: ._curl, - record: isRecording, + record: record, timeout: timeout, syntaxDescriptor: syntaxDescriptor, matches: expected, diff --git a/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4a7be132..d55a2692 100644 --- a/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6", - "version" : "1.3.0" + "revision" : "ae33e5941bb88d88538d0a6b19ca0b01e6c76dcf", + "version" : "1.3.1" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "bc92c4b27f9a84bfb498cdbfdf35d5a357e9161f", - "version" : "1.5.6" + "revision" : "19b7263bacb9751f151ec0c93ec816fe1ef67c7b", + "version" : "1.6.1" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "163409ef7dae9d960b87f34b51587b6609a76c1f", - "version" : "1.3.0" + "revision" : "82a4ae7170d98d8538ec77238b7eb8e7199ef2e8", + "version" : "1.3.1" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-identified-collections.git", "state" : { - "revision" : "2f5ab6e091dd032b63dacbda052405756010dc3b", - "version" : "1.1.0" + "revision" : "322d9ffeeba85c9f7c4984b39422ec7cc3c56597", + "version" : "1.1.1" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7", - "version" : "1.17.6" + "revision" : "f5bfff796ee8e3bc9a685b7ffba1bf20663eb370", + "version" : "1.18.0" } }, { @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", - "version" : "1.4.3" + "revision" : "b444594f79844b0d6d76d70fbfb3f7f71728f938", + "version" : "1.5.1" } } ], diff --git a/Tests/IntegrationTests/AuthClientIntegrationTests.swift b/Tests/IntegrationTests/AuthClientIntegrationTests.swift index d72637c8..4bc61ba0 100644 --- a/Tests/IntegrationTests/AuthClientIntegrationTests.swift +++ b/Tests/IntegrationTests/AuthClientIntegrationTests.swift @@ -9,6 +9,7 @@ import ConcurrencyExtras import CustomDump import TestHelpers +import InlineSnapshotTesting import XCTest #if canImport(FoundationNetworking) @@ -74,6 +75,23 @@ final class AuthClientIntegrationTests: XCTestCase { } } + func testSignUpWithWeakPassword() async { + do { + try await authClient.signUp(email: mockEmail(), password: "weak-password") + } catch { + assertInlineSnapshot(of: error, as: .dump) { + #""" + ▿ AuthError + ▿ weakPassword: (2 elements) + - message: "Password should contain at least one character of each: abcdefghijklmnopqrstuvwxyz, ABCDEFGHIJKLMNOPQRSTUVWXYZ, 0123456789, !@#$%^&*()_+-=[]{};\':\"|<>?,./`~." + ▿ reasons: 1 element + - "characters" + + """# + } + } + } + // func testSignUpAndSignInWithPhone() async throws { // try await XCTAssertAuthChangeEvents([.initialSession, .signedIn, .signedOut, .signedIn]) { // let phone = mockPhoneNumber() @@ -167,12 +185,6 @@ final class AuthClientIntegrationTests: XCTestCase { } } - func testUserIdentities() async throws { - let session = try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword()) - let identities = try await authClient.userIdentities() - expectNoDifference(session.user.identities, identities) - } - func testUnlinkIdentity_withOnlyOneIdentity() async throws { let identities = try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword()) .user.identities @@ -246,8 +258,8 @@ final class AuthClientIntegrationTests: XCTestCase { func testLinkIdentity() async throws { try await signUpIfNeededOrSignIn(email: mockEmail(), password: mockPassword()) - try await authClient.linkIdentity(provider: .github) { url in - XCTAssertTrue(url.absoluteString.contains("github.com")) + try await authClient.linkIdentity(provider: .apple) { url in + XCTAssertTrue(url.absoluteString.contains("apple.com")) } } @@ -318,17 +330,24 @@ final class AuthClientIntegrationTests: XCTestCase { } private func mockPassword(length: Int = 12) -> String { - let allowedCharacters = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+" + let allowedCharacters = [ + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "0123456789", + "!@#$%^&*()_+-=[]{};\':\"|<>?,./`~.", + ] + var password = "" - for _ in 0 ..< length { - let randomIndex = Int.random(in: 0 ..< allowedCharacters.count) - let character = allowedCharacters[allowedCharacters.index( - allowedCharacters.startIndex, - offsetBy: randomIndex - )] - password.append(character) + while password.count < length { + for characterSet in allowedCharacters { + let randomIndex = Int.random(in: 0 ..< characterSet.count) + let character = characterSet[characterSet.index( + characterSet.startIndex, + offsetBy: randomIndex + )] + password.append(character) + } } return password diff --git a/Tests/IntegrationTests/DotEnv.swift b/Tests/IntegrationTests/DotEnv.swift new file mode 100644 index 00000000..d0f26e79 --- /dev/null +++ b/Tests/IntegrationTests/DotEnv.swift @@ -0,0 +1,6 @@ + +enum DotEnv { + static let SUPABASE_URL = "http://localhost:54321" + static let SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" + static let SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU" +} \ No newline at end of file diff --git a/Tests/IntegrationTests/Postgrest/PostgresTransformsTests.swift b/Tests/IntegrationTests/Postgrest/PostgresTransformsTests.swift index a0c5925e..fc45afdb 100644 --- a/Tests/IntegrationTests/Postgrest/PostgresTransformsTests.swift +++ b/Tests/IntegrationTests/Postgrest/PostgresTransformsTests.swift @@ -44,6 +44,20 @@ final class PostgrestTransformsTests: XCTestCase { "status" : "OFFLINE", "username" : "kiwicopple" }, + { + "age_range" : "[20,30)", + "catchphrase" : "'json' 'test'", + "data" : { + "foo" : { + "bar" : { + "nested" : "value" + }, + "baz" : "string value" + } + }, + "status" : "ONLINE", + "username" : "jsonuser" + }, { "age_range" : "[20,30)", "catchphrase" : "'fat' 'rat'", @@ -74,6 +88,13 @@ final class PostgrestTransformsTests: XCTestCase { assertInlineSnapshot(of: res, as: .json) { """ [ + { + "channel_id" : 3, + "data" : null, + "id" : 4, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 2, "data" : null, @@ -265,7 +286,7 @@ final class PostgrestTransformsTests: XCTestCase { let res = try await client.from("users").select().csv().execute().string() assertInlineSnapshot(of: res, as: .json) { #""" - "username,data,age_range,status,catchphrase\nsupabot,,\"[1,2)\",ONLINE,\"'cat' 'fat'\"\nkiwicopple,,\"[25,35)\",OFFLINE,\"'bat' 'cat'\"\nawailas,,\"[25,35)\",ONLINE,\"'bat' 'rat'\"\ndragarcia,,\"[20,30)\",ONLINE,\"'fat' 'rat'\"" + "username,data,age_range,status,catchphrase\nsupabot,,\"[1,2)\",ONLINE,\"'cat' 'fat'\"\nkiwicopple,,\"[25,35)\",OFFLINE,\"'bat' 'cat'\"\nawailas,,\"[25,35)\",ONLINE,\"'bat' 'rat'\"\ndragarcia,,\"[20,30)\",ONLINE,\"'fat' 'rat'\"\njsonuser,\"{\"\"foo\"\": {\"\"bar\"\": {\"\"nested\"\": \"\"value\"\"}, \"\"baz\"\": \"\"string value\"\"}}\",\"[20,30)\",ONLINE,\"'json' 'test'\"" """# } } diff --git a/Tests/IntegrationTests/Postgrest/PostgrestBasicTests.swift b/Tests/IntegrationTests/Postgrest/PostgrestBasicTests.swift index 9912b4d1..9bec32e8 100644 --- a/Tests/IntegrationTests/Postgrest/PostgrestBasicTests.swift +++ b/Tests/IntegrationTests/Postgrest/PostgrestBasicTests.swift @@ -52,6 +52,20 @@ final class PostgrestBasicTests: XCTestCase { "data" : null, "status" : "ONLINE", "username" : "dragarcia" + }, + { + "age_range" : "[20,30)", + "catchphrase" : "'json' 'test'", + "data" : { + "foo" : { + "bar" : { + "nested" : "value" + }, + "baz" : "string value" + } + }, + "status" : "ONLINE", + "username" : "jsonuser" } ] """ @@ -78,6 +92,10 @@ final class PostgrestBasicTests: XCTestCase { { "non_updatable_column" : 1, "username" : "dragarcia" + }, + { + "non_updatable_column" : 1, + "username" : "jsonuser" } ] """ @@ -147,6 +165,12 @@ final class PostgrestBasicTests: XCTestCase { "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" }, + { + "channel_id" : 3, + "data" : null, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 1, "data" : null, @@ -202,6 +226,12 @@ final class PostgrestBasicTests: XCTestCase { "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" }, + { + "channel_id" : 3, + "data" : null, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 1, "data" : null, @@ -265,6 +295,12 @@ final class PostgrestBasicTests: XCTestCase { "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" }, + { + "channel_id" : 3, + "data" : null, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 1, "data" : null, @@ -347,6 +383,12 @@ final class PostgrestBasicTests: XCTestCase { "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" }, + { + "channel_id" : 3, + "data" : null, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 2, "data" : null, @@ -430,6 +472,13 @@ final class PostgrestBasicTests: XCTestCase { "id" : 2, "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" + }, + { + "channel_id" : 3, + "data" : null, + "id" : 4, + "message" : "Some message on channel wihtout details", + "username" : "supabot" } ] """ diff --git a/Tests/IntegrationTests/Postgrest/PostgrestFilterTests.swift b/Tests/IntegrationTests/Postgrest/PostgrestFilterTests.swift index 7cc624d9..0bcbbcbe 100644 --- a/Tests/IntegrationTests/Postgrest/PostgrestFilterTests.swift +++ b/Tests/IntegrationTests/Postgrest/PostgrestFilterTests.swift @@ -36,6 +36,9 @@ final class PostgrestFilterTests: XCTestCase { { "status" : "ONLINE" }, + { + "status" : "ONLINE" + }, { "status" : "ONLINE" } @@ -103,6 +106,9 @@ final class PostgrestFilterTests: XCTestCase { }, { "username" : "dragarcia" + }, + { + "username" : "jsonuser" } ] """ @@ -121,6 +127,9 @@ final class PostgrestFilterTests: XCTestCase { [ { "id" : 2 + }, + { + "id" : 4 } ] """ @@ -142,6 +151,9 @@ final class PostgrestFilterTests: XCTestCase { }, { "id" : 2 + }, + { + "id" : 4 } ] """ @@ -346,6 +358,9 @@ final class PostgrestFilterTests: XCTestCase { { "status" : "ONLINE" }, + { + "status" : "ONLINE" + }, { "status" : "ONLINE" } @@ -457,6 +472,9 @@ final class PostgrestFilterTests: XCTestCase { { "age_range" : "[25,35)" }, + { + "age_range" : "[20,30)" + }, { "age_range" : "[20,30)" } @@ -497,6 +515,9 @@ final class PostgrestFilterTests: XCTestCase { assertInlineSnapshot(of: res, as: .json) { """ [ + { + "age_range" : "[20,30)" + }, { "age_range" : "[20,30)" } diff --git a/Tests/IntegrationTests/Postgrest/PostgrestResourceEmbeddingTests.swift b/Tests/IntegrationTests/Postgrest/PostgrestResourceEmbeddingTests.swift index c4acfbda..203f4622 100644 --- a/Tests/IntegrationTests/Postgrest/PostgrestResourceEmbeddingTests.swift +++ b/Tests/IntegrationTests/Postgrest/PostgrestResourceEmbeddingTests.swift @@ -41,6 +41,13 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { "id" : 2, "message" : "Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.", "username" : "supabot" + }, + { + "channel_id" : 3, + "data" : null, + "id" : 4, + "message" : "Some message on channel wihtout details", + "username" : "supabot" } ] }, @@ -57,6 +64,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -97,6 +109,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -144,6 +161,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -191,6 +213,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -209,6 +236,13 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { [ { "messages" : [ + { + "channel_id" : 3, + "data" : null, + "id" : 4, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 2, "data" : null, @@ -238,6 +272,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -257,6 +296,13 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { [ { "messages" : [ + { + "channel_id" : 3, + "data" : null, + "id" : 4, + "message" : "Some message on channel wihtout details", + "username" : "supabot" + }, { "channel_id" : 2, "data" : null, @@ -286,6 +332,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -326,6 +377,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] @@ -366,6 +422,11 @@ final class PostgrestResourceEmbeddingTests: XCTestCase { { "messages" : [ + ] + }, + { + "messages" : [ + ] } ] diff --git a/Tests/IntegrationTests/RealtimeIntegrationTests.swift b/Tests/IntegrationTests/RealtimeIntegrationTests.swift index d153b9e1..dd28b5f2 100644 --- a/Tests/IntegrationTests/RealtimeIntegrationTests.swift +++ b/Tests/IntegrationTests/RealtimeIntegrationTests.swift @@ -30,7 +30,10 @@ final class RealtimeIntegrationTests: XCTestCase { let client = SupabaseClient( supabaseURL: URL(string: DotEnv.SUPABASE_URL) ?? URL(string: "http://localhost:54321")!, - supabaseKey: DotEnv.SUPABASE_ANON_KEY + supabaseKey: DotEnv.SUPABASE_ANON_KEY, + options: SupabaseClientOptions( + auth: SupabaseClientOptions.AuthOptions(storage: InMemoryLocalStorage()) + ) ) override func setUp() { @@ -210,12 +213,12 @@ final class RealtimeIntegrationTests: XCTestCase { _ = await channel.system().first(where: { _ in true }) let key = try await - (client.from("key_value_storage") + (client.from("key_value") .insert(["key": AnyJSON.string(UUID().uuidString), "value": "value1"]).select().single() .execute().value as Entry).key - try await client.from("key_value_storage").update(["value": "value2"]).eq("key", value: key) + try await client.from("key_value").update(["value": "value2"]).eq("key", value: key) .execute() - try await client.from("key_value_storage").delete().eq("key", value: key).execute() + try await client.from("key_value").delete().eq("key", value: key).execute() let insertedEntries = try await receivedInsertActions.value.map { try $0.decodeRecord( diff --git a/Tests/IntegrationTests/StorageFileIntegrationTests.swift b/Tests/IntegrationTests/StorageFileIntegrationTests.swift index 188090a9..e869319c 100644 --- a/Tests/IntegrationTests/StorageFileIntegrationTests.swift +++ b/Tests/IntegrationTests/StorageFileIntegrationTests.swift @@ -382,7 +382,7 @@ final class StorageFileIntegrationTests: XCTestCase { let httpResponse = try XCTUnwrap(response as? HTTPURLResponse) let cacheControl = try XCTUnwrap(httpResponse.value(forHTTPHeaderField: "cache-control")) - XCTAssertEqual(cacheControl, "public, max-age=14400") + XCTAssertEqual(cacheControl, "max-age=14400") } func testUploadWithFileURL() async throws { diff --git a/supabase/.gitignore b/Tests/IntegrationTests/supabase/.gitignore similarity index 100% rename from supabase/.gitignore rename to Tests/IntegrationTests/supabase/.gitignore diff --git a/Tests/IntegrationTests/supabase/.temp/cli-latest b/Tests/IntegrationTests/supabase/.temp/cli-latest index e4af78e3..8493f9f9 100644 --- a/Tests/IntegrationTests/supabase/.temp/cli-latest +++ b/Tests/IntegrationTests/supabase/.temp/cli-latest @@ -1 +1 @@ -v2.6.8 \ No newline at end of file +v2.9.6 \ No newline at end of file diff --git a/supabase/config.toml b/Tests/IntegrationTests/supabase/config.toml similarity index 52% rename from supabase/config.toml rename to Tests/IntegrationTests/supabase/config.toml index 088ad885..7916e7f7 100644 --- a/supabase/config.toml +++ b/Tests/IntegrationTests/supabase/config.toml @@ -7,14 +7,18 @@ enabled = true # Port to use for the API URL. port = 54321 # Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. public and storage are always included. -schemas = ["public", "storage", "graphql_public"] -# Extra schemas to add to the search_path of every request. public is always included. +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. extra_search_path = ["public", "extensions"] # The maximum number of rows returns from a view, table, or stored procedure. Limits payload size # for accidental or malicious requests. max_rows = 1000 +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false + [db] # Port to use for the local database URL. port = 54322 @@ -36,9 +40,19 @@ default_pool_size = 20 # Maximum number of client connections allowed. max_client_conn = 100 +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory: "./seeds/*.sql" +sql_paths = ["./seed.sql"] + [realtime] enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv6) +# Bind realtime via either IPv4 or IPv6. (default: IPv4) # ip_version = "IPv6" # The maximum length in bytes of HTTP request headers. (default: 4096) # max_header_length = 4096 @@ -61,12 +75,25 @@ port = 54324 # Uncomment to expose additional ports for testing user applications that send emails. # smtp_port = 54325 # pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" [storage] enabled = true # The maximum file size allowed (e.g. "5MB", "500KB"). file_size_limit = "50MiB" +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + [auth] enabled = true # The base URL of your website. Used as an allow-list for redirects and for constructing URLs used @@ -83,8 +110,21 @@ enable_refresh_token_rotation = true refresh_token_reuse_interval = 10 # Allow/disallow new user signups to your project. enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = true # Allow/disallow testing manual linking of accounts enable_manual_linking = true +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "lower_upper_letters_digits_symbols" + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" [auth.email] # Allow/disallow new user signups via email to your project. @@ -94,6 +134,24 @@ enable_signup = true double_confirm_changes = true # If enabled, users need to confirm their email address before signing in. enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" # Uncomment to customize email template # [auth.email.template.invite] @@ -102,22 +160,30 @@ enable_confirmations = false [auth.sms] # Allow/disallow new user signups via SMS to your project. -enable_signup = true +enable_signup = false # If enabled, users need to confirm their phone number before signing in. enable_confirmations = false # Template for sending OTP to users -template = "Your code is {{ .Code }} ." +template = "Your code is {{ `{{ .Code }}` }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" # Use pre-defined map of phone number to OTP for testing. [auth.sms.test_otp] 4152127777 = "123456" +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + # This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. -[auth.hook.custom_access_token] +# [auth.hook.custom_access_token] # enabled = true # uri = "pg-functions:////" - # Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. [auth.sms.twilio] enabled = false @@ -126,36 +192,84 @@ message_service_sid = "" # DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ `{{ .Code }}` }}" +max_frequency = "5s" + +# Configure MFA via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + # Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, # `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, # `twitter`, `slack`, `spotify`, `workos`, `zoom`. [auth.external.apple] -enabled = false -client_id = "" +enabled = true +client_id = "client id" # DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +secret = "client secret" # Overrides the default auth redirectUrl. redirect_uri = "" # Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, # or any other third-party OIDC providers. url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false -[analytics] +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +[edge_runtime] +enabled = true +# Configure one of the supported request policies: `oneshot`, `per_worker`. +# Use `oneshot` for hot reload, or `per_worker` for load testing. +policy = "oneshot" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 + +# Use these configurations to customize your Edge Function. +# [functions.MY_FUNCTION_NAME] +# enabled = true +# verify_jwt = true +# import_map = "./functions/MY_FUNCTION_NAME/deno.json" +# Uncomment to specify a custom file path to the entrypoint. +# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx +# entrypoint = "./functions/MY_FUNCTION_NAME/index.ts" +# Specifies static files to be bundled with the function. Supports glob patterns. +# For example, if you want to serve static HTML pages in your function: +# static_files = [ "./functions/MY_FUNCTION_NAME/*.html" ] + +[analytics] +enabled = true port = 54327 -vector_port = 54328 # Configure one of the supported backends: `postgres`, `bigquery`. backend = "postgres" - -# Experimental features may be deprecated any time -[experimental] -# Configures Postgres storage engine to use OrioleDB (S3) -orioledb_version = "" -# Configures S3 bucket URL, eg. .s3-.amazonaws.com -s3_host = "env(S3_HOST)" -# Configures S3 bucket region, eg. us-east-1 -s3_region = "env(S3_REGION)" -# Configures AWS_ACCESS_KEY_ID for S3 bucket -s3_access_key = "env(S3_ACCESS_KEY)" -# Configures AWS_SECRET_ACCESS_KEY for S3 bucket -s3_secret_key = "env(S3_SECRET_KEY)" diff --git a/Tests/IntegrationTests/supabase/migrations/20240327182636_init.sql b/Tests/IntegrationTests/supabase/migrations/20240327182636_init.sql new file mode 100644 index 00000000..30f8ce35 --- /dev/null +++ b/Tests/IntegrationTests/supabase/migrations/20240327182636_init.sql @@ -0,0 +1,174 @@ +-- Create a second schema +CREATE SCHEMA personal; + +-- USERS +CREATE TYPE public.user_status AS ENUM ('ONLINE', 'OFFLINE'); +CREATE TABLE public.users ( + username text primary key, + data jsonb DEFAULT null, + age_range int4range DEFAULT null, + status user_status DEFAULT 'ONLINE'::public.user_status, + catchphrase tsvector DEFAULT null +); +ALTER TABLE public.users REPLICA IDENTITY FULL; -- Send "previous data" to supabase +COMMENT ON COLUMN public.users.data IS 'For unstructured data and prototyping.'; + + +-- CREATE A ZERO-TO-ONE RELATIONSHIP (User can have profile, but not all of them do) +CREATE TABLE public.user_profiles ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + username text REFERENCES users +); + +-- CREATE A TABLE WITH TWO RELATIONS TO SAME DESTINATION WHICH WILL NEED HINTING +CREATE TABLE public.best_friends ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + -- Thoses relations should always be satisfied, never be null + first_user text REFERENCES users NOT NULL, + second_user text REFERENCES users NOT NULL, + -- This relation is nullable, it might be null + third_wheel text REFERENCES users +); + + +-- CHANNELS +CREATE TABLE public.channels ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + data jsonb DEFAULT null, + slug text +); +ALTER TABLE public.users REPLICA IDENTITY FULL; -- Send "previous data" to supabase +COMMENT ON COLUMN public.channels.data IS 'For unstructured data and prototyping.'; + +create table public.channel_details ( + id bigint primary key references channels(id), + details text default null +); + +-- MESSAGES +CREATE TABLE public.messages ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + data jsonb DEFAULT null, + message text, + username text REFERENCES users NOT NULL, + channel_id bigint REFERENCES channels NOT NULL +); +ALTER TABLE public.messages REPLICA IDENTITY FULL; -- Send "previous data" to supabase +COMMENT ON COLUMN public.messages.data IS 'For unstructured data and prototyping.'; + +-- SELF REFERENCING TABLE +CREATE TABLE public.collections ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + description text, + parent_id bigint +); +ALTER TABLE public.messages REPLICA IDENTITY FULL; -- Send "previous data" to supabase +-- SELF REFERENCE via parent_id +ALTER TABLE public.collections +ADD CONSTRAINT collections_parent_id_fkey +FOREIGN KEY (parent_id) +REFERENCES public.collections(id); +COMMENT ON COLUMN public.messages.data IS 'For unstructured data and prototyping.'; + +-- MANY-TO-MANY RELATIONSHIP USING A JOIN TABLE + +-- Create a table for products +CREATE TABLE public.products ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name text NOT NULL, + description text, + price decimal(10, 2) NOT NULL +); + +-- Create a table for categories +CREATE TABLE public.categories ( + id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name text NOT NULL, + description text +); + +-- Create a join table for the many-to-many relationship between products and categories +CREATE TABLE public.product_categories ( + product_id bigint REFERENCES public.products(id) ON DELETE CASCADE, + category_id bigint REFERENCES public.categories(id) ON DELETE CASCADE, + PRIMARY KEY (product_id, category_id) +); + + +-- STORED FUNCTION +CREATE FUNCTION public.get_status(name_param text) +RETURNS user_status AS $$ + SELECT status from users WHERE username=name_param; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE FUNCTION public.get_username_and_status(name_param text) +RETURNS TABLE(username text, status user_status) AS $$ + SELECT username, status from users WHERE username=name_param; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE FUNCTION public.offline_user(name_param text) +RETURNS user_status AS $$ + UPDATE users SET status = 'OFFLINE' WHERE username=name_param + RETURNING status; +$$ LANGUAGE SQL VOLATILE; + +CREATE FUNCTION public.void_func() +RETURNS void AS $$ +$$ LANGUAGE SQL; + +create extension postgis schema extensions; + +create table public.shops ( + id int primary key +, address text +, shop_geom extensions.geometry(POINT, 4326) +); + +create view public.non_updatable_view as + select username from public.users limit 1; + +create view public.updatable_view as + select username, 1 as non_updatable_column from public.users; + +-- SECOND SCHEMA USERS +CREATE TYPE personal.user_status AS ENUM ('ONLINE', 'OFFLINE'); +CREATE TABLE personal.users( + username text primary key, + data jsonb DEFAULT null, + age_range int4range DEFAULT null, + status user_status DEFAULT 'ONLINE'::public.user_status +); + +-- SECOND SCHEMA STORED FUNCTION +CREATE FUNCTION personal.get_status(name_param text) +RETURNS user_status AS $$ + SELECT status from users WHERE username=name_param; +$$ LANGUAGE SQL IMMUTABLE; + +create function public.function_with_optional_param(param text default '') +returns text as $$ + select param; +$$ language sql immutable; + +create function public.function_with_array_param(param uuid[]) +returns void as '' language sql immutable; + + +create table public.cornercase ( + id int primary key, + "column whitespace" text, + array_column text[] +); + +create function public.get_array_element(arr int[], index int) +returns int as $$ + select arr[index]; +$$ language sql immutable; + + +create table key_value ( + key text primary key, + value jsonb +); + +alter publication supabase_realtime add table key_value; \ No newline at end of file diff --git a/Tests/IntegrationTests/supabase/seed.sql b/Tests/IntegrationTests/supabase/seed.sql new file mode 100644 index 00000000..1ad59ca8 --- /dev/null +++ b/Tests/IntegrationTests/supabase/seed.sql @@ -0,0 +1,89 @@ +INSERT INTO + public.users (username, status, age_range, catchphrase, data) +VALUES + ('supabot', 'ONLINE', '[1,2)'::int4range, 'fat cat'::tsvector, NULL), + ('kiwicopple', 'OFFLINE', '[25,35)'::int4range, 'cat bat'::tsvector, NUlL), + ('awailas', 'ONLINE', '[25,35)'::int4range, 'bat rat'::tsvector, NULL), + ('dragarcia', 'ONLINE', '[20,30)'::int4range, 'rat fat'::tsvector, NULL), + ('jsonuser', 'ONLINE', '[20,30)'::int4range, 'json test'::tsvector, '{"foo": {"bar": {"nested": "value"}, "baz": "string value"}}'::jsonb); + +INSERT INTO + public.channels (slug) +VALUES + ('public'), + ('random'), + ('other'); + +INSERT INTO + public.messages (message, channel_id, username) +VALUES + ('Hello World 👋', 1, 'supabot'), + ('Perfection is attained, not when there is nothing more to add, but when there is nothing left to take away.', 2, 'supabot'), + ('Some message on channel wihtout details', 3, 'supabot'), + ('Some message on channel wihtout details', 3, 'supabot'); + +INSERT INTO + personal.users (username, status, age_range) +VALUES + ('supabot', 'ONLINE', '[1,2)'::int4range), + ('kiwicopple', 'OFFLINE', '[25,35)'::int4range), + ('awailas', 'ONLINE', '[25,35)'::int4range), + ('dragarcia', 'ONLINE', '[20,30)'::int4range), + ('leroyjenkins', 'ONLINE', '[20,40)'::int4range); + +INSERT INTO shops(id, address, shop_geom) +VALUES + (1, '1369 Cambridge St', 'SRID=4326;POINT(-71.10044 42.373695)'); + +INSERT INTO public.channel_details (id, details) +VALUES + (1, 'Details for public channel'), + (2, 'Details for random channel'); + +INSERT INTO user_profiles (id, username) +VALUES + (1, 'supabot'), + (2, NULL); + +INSERT INTO best_friends(id, first_user, second_user, third_wheel) +VALUES + (1, 'supabot', 'kiwicopple', 'awailas'), + (2, 'supabot', 'awailas', NULL); + +INSERT INTO public.collections (id, description, parent_id) +VALUES + (1, 'Root Collection', NULL), + (2, 'Child of Root', 1), + (3, 'Another Child of Root', 1), + (4, 'Grandchild', 2), + (5, 'Sibling of Grandchild', 2), + (6, 'Child of Another Root', 3); + +-- Insert sample products +INSERT INTO public.products (id, name, description, price) +VALUES + (1, 'Laptop', 'High-performance laptop', 999.99), + (2, 'Smartphone', 'Latest model smartphone', 699.99), + (3, 'Headphones', 'Noise-cancelling headphones', 199.99); + +-- Insert sample categories +INSERT INTO public.categories (id, name, description) +VALUES + (1, 'Electronics', 'Electronic devices and gadgets'), + (2, 'Computers', 'Computer and computer accessories'), + (3, 'Audio', 'Audio equipment'); + +-- Insert product-category relationships +INSERT INTO public.product_categories (product_id, category_id) +VALUES + (1, 1), -- Laptop is in Electronics + (1, 2), -- Laptop is also in Computers + (2, 1), -- Smartphone is in Electronics + (3, 1), -- Headphones are in Electronics + (3, 3); -- Headphones are also in Audio + +INSERT INTO public.cornercase (id, array_column) +VALUES + (1, ARRAY['test', 'one']), + (2, ARRAY['another']), + (3, ARRAY['test2']); \ No newline at end of file diff --git a/Tests/PostgRESTTests/PostgresQueryTests.swift b/Tests/PostgRESTTests/PostgresQueryTests.swift index 4b30f702..16edcd95 100644 --- a/Tests/PostgRESTTests/PostgresQueryTests.swift +++ b/Tests/PostgRESTTests/PostgresQueryTests.swift @@ -11,6 +11,10 @@ import PostgREST import TestHelpers import XCTest +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + class PostgrestQueryTests: XCTestCase { let url = URL(string: "http://localhost:54321/rest/v1")! diff --git a/Tests/StorageTests/StorageBucketAPITests.swift b/Tests/StorageTests/StorageBucketAPITests.swift index c79b2f34..e8b3edc6 100644 --- a/Tests/StorageTests/StorageBucketAPITests.swift +++ b/Tests/StorageTests/StorageBucketAPITests.swift @@ -5,6 +5,10 @@ import XCTest @testable import Storage +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + final class StorageBucketAPITests: XCTestCase { let url = URL(string: "http://localhost:54321/storage/v1")! var storage: SupabaseStorageClient! diff --git a/Tests/StorageTests/StorageFileAPITests.swift b/Tests/StorageTests/StorageFileAPITests.swift index 3751f8c8..8a8bf785 100644 --- a/Tests/StorageTests/StorageFileAPITests.swift +++ b/Tests/StorageTests/StorageFileAPITests.swift @@ -6,6 +6,10 @@ import XCTest @testable import Storage +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + final class StorageFileAPITests: XCTestCase { let url = URL(string: "http://localhost:54321/storage/v1")! var storage: SupabaseStorageClient! diff --git a/supabase/migrations/20240327182636_init_key_value_storage_schema.sql b/supabase/migrations/20240327182636_init_key_value_storage_schema.sql deleted file mode 100644 index 80591183..00000000 --- a/supabase/migrations/20240327182636_init_key_value_storage_schema.sql +++ /dev/null @@ -1,8 +0,0 @@ -create table key_value_storage( - "key" text primary key, - "value" jsonb not null -); - -alter publication supabase_realtime - add table key_value_storage; - diff --git a/supabase/seed.sql b/supabase/seed.sql deleted file mode 100644 index e69de29b..00000000