From 6fc493864c1ee804b7fdc1429758325f87fe2bb6 Mon Sep 17 00:00:00 2001 From: Jermaine Date: Sun, 16 Mar 2025 07:11:33 -0400 Subject: [PATCH 1/4] fix: selecting item with filter in empty table --- lib/src/mock_supabase_http_client.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/mock_supabase_http_client.dart b/lib/src/mock_supabase_http_client.dart index ebeab1c..1cccb9d 100644 --- a/lib/src/mock_supabase_http_client.dart +++ b/lib/src/mock_supabase_http_client.dart @@ -541,7 +541,7 @@ class MockSupabaseHttpClient extends BaseClient { }).toList(); } else if (key.contains('!inner')) { // referenced table filtering with !inner - } else { + } else if (returningRows.isNotEmpty) { // Regular filtering on the top level table final filter = FilterParser.parseFilter( columnName: key, From 828178fddbbc0208c3d85b372f18cba85ed02486 Mon Sep 17 00:00:00 2001 From: Jermaine Date: Sun, 16 Mar 2025 07:11:40 -0400 Subject: [PATCH 2/4] test: add case for selecting after deleting all items --- test/mock_supabase_http_client_test.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/mock_supabase_http_client_test.dart b/test/mock_supabase_http_client_test.dart index 63a158d..8b8b84a 100644 --- a/test/mock_supabase_http_client_test.dart +++ b/test/mock_supabase_http_client_test.dart @@ -255,6 +255,16 @@ void main() { expect(titlesOnly[1], {'id': 1, 'title': 'First post'}); }); + test('Select after deleting all items', () async { + // Insert an item + await mockSupabase + .from('posts') + .insert({'id': 1, 'title': 'To be deleted'}); + await mockSupabase.from('posts').delete().eq('id', 1); + final posts = await mockSupabase.from('posts').select().eq('id', 1); + expect(posts.length, 0); + }); + test('No mixing with default schema', () async { // Insert into custom schema await mockSupabase From f8502137441c9564f0051c0b4b94458a422d3958 Mon Sep 17 00:00:00 2001 From: Jermaine Date: Sun, 16 Mar 2025 07:14:59 -0400 Subject: [PATCH 3/4] test: move test to basic CRUD tests --- test/mock_supabase_http_client_test.dart | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/mock_supabase_http_client_test.dart b/test/mock_supabase_http_client_test.dart index 8b8b84a..90db271 100644 --- a/test/mock_supabase_http_client_test.dart +++ b/test/mock_supabase_http_client_test.dart @@ -122,6 +122,16 @@ void main() { expect(posts.length, 0); }); + test('Select after deleting all items', () async { + // Insert an item + await mockSupabase + .from('posts') + .insert({'id': 1, 'title': 'To be deleted'}); + await mockSupabase.from('posts').delete().eq('id', 1); + final posts = await mockSupabase.from('posts').select().eq('id', 1); + expect(posts.length, 0); + }); + test('Select all columns', () async { // Test selecting all records await mockSupabase.from('posts').insert([ @@ -255,16 +265,6 @@ void main() { expect(titlesOnly[1], {'id': 1, 'title': 'First post'}); }); - test('Select after deleting all items', () async { - // Insert an item - await mockSupabase - .from('posts') - .insert({'id': 1, 'title': 'To be deleted'}); - await mockSupabase.from('posts').delete().eq('id', 1); - final posts = await mockSupabase.from('posts').select().eq('id', 1); - expect(posts.length, 0); - }); - test('No mixing with default schema', () async { // Insert into custom schema await mockSupabase From dd6dfd58b5e4eecf26d60820b89ed85eeeb3adab Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 9 Sep 2026 14:36:00 +0200 Subject: [PATCH 4/4] fix: skip filtering when no rows are left The referenced table branch dereferences returningRows.first the same way the top level branch does, so filtering on a referenced table column in an empty table still threw a Bad state error. Guarding at the top of the loop covers both branches, as well as the case where an earlier filter leaves no rows behind. --- lib/src/mock_supabase_http_client.dart | 5 ++++- test/mock_supabase_http_client_test.dart | 28 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/src/mock_supabase_http_client.dart b/lib/src/mock_supabase_http_client.dart index 1cccb9d..c5c5b45 100644 --- a/lib/src/mock_supabase_http_client.dart +++ b/lib/src/mock_supabase_http_client.dart @@ -499,6 +499,9 @@ class MockSupabaseHttpClient extends BaseClient { // Handle basic filtering queryParams.forEach((key, value) { + if (returningRows.isEmpty) { + return; + } if (key != 'select' && key != 'order' && key != 'limit' && @@ -541,7 +544,7 @@ class MockSupabaseHttpClient extends BaseClient { }).toList(); } else if (key.contains('!inner')) { // referenced table filtering with !inner - } else if (returningRows.isNotEmpty) { + } else { // Regular filtering on the top level table final filter = FilterParser.parseFilter( columnName: key, diff --git a/test/mock_supabase_http_client_test.dart b/test/mock_supabase_http_client_test.dart index 90db271..86fbf2b 100644 --- a/test/mock_supabase_http_client_test.dart +++ b/test/mock_supabase_http_client_test.dart @@ -132,6 +132,34 @@ void main() { expect(posts.length, 0); }); + test('Select with a referenced table filter after deleting all items', + () async { + // Insert an item + await mockSupabase.from('posts').insert({ + 'id': 1, + 'title': 'To be deleted', + 'authors': {'id': 1, 'name': 'Author One'} + }); + await mockSupabase.from('posts').delete().eq('id', 1); + final posts = await mockSupabase + .from('posts') + .select('*, authors(*)') + .eq('authors.name', 'Author One'); + expect(posts.length, 0); + }); + + test('Select with chained filters where the first returns no rows', + () async { + // Insert an item + await mockSupabase.from('posts').insert({'id': 1, 'title': 'First post'}); + final posts = await mockSupabase + .from('posts') + .select() + .eq('id', 2) + .eq('title', 'First post'); + expect(posts.length, 0); + }); + test('Select all columns', () async { // Test selecting all records await mockSupabase.from('posts').insert([