-
-
Notifications
You must be signed in to change notification settings - Fork 5
fix: Add some more RETURNING clause tests, and fix JSON encoding and … #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
011a05a
fix: Add some more RETURNING clause tests, and fix JSON encoding and …
ocean f4f0265
feat: Add ecto_sqlite3 compatibility test suite
ocean 47125bf
docs: Add comprehensive ecto_sqlite3 compatibility testing documentation
ocean ab0c6f3
fix: Switch ecto_sqlite3 compat tests to manual table creation
ocean 4a2ddf9
docs: Update compatibility testing status and findings
ocean 71cd4eb
docs: Add comprehensive session summary
ocean 89402b5
Fix ecto_libsql compatibility tests - Timestamps, JSON, and BLOB tests
ocean 36795a0
Add comprehensive session summary for compatibility test fixes
ocean 44c813f
Apply formatter fixes to all modified files
ocean 79c5443
Add datetime roundtrip assertions in type_compatibility_test
ocean 4dd6aac
fix: Add per-test cleanup and timestamp columns to CRUD compatibility…
ocean 51ff6be
docs: Add SQLite-specific query limitations and compatibility testing…
ocean 0c08926
Fix: Handle :serial and :bigserial types in migrations
ocean ea3b047
refactor: Add explicit nil handling in json_encode/1 function
ocean 6419a18
refactor: Simplify redundant assertion condition in returning_test.exs
ocean 12c4d50
style: Format ecto_sqlite3_returning_debug_test.exs for code style co…
ocean dd49e41
docs: Add completion summary for CI fixes
ocean 36f534c
fix: Remove unused variables and imports in test files
ocean c24a071
refactor: Improve AccountUser schema with associations and validation
ocean ccab556
refactor: Address code review feedback and improve test quality
ocean 95a3ae2
feat: Add CHECK constraint support for column-level constraints
ocean f484d4f
fix: Add datetime microsecond type loading support
ocean 441fc97
docs: Update CHANGELOG with summary of unreleased changes
ocean d38d7bf
feat: Add comprehensive type loader/dumper support
ocean e5d0cfb
chore: Remove unneeded docs
ocean 9dcdddb
chore: Sync beads with type enhancement issues
ocean 7d5f7c2
docs: Fix incorrect BLOB null byte claim and add missing comma
ocean d160e3b
test: Fix microsecond precision assertions in datetime tests
ocean a5f565f
test: Add assertion for update result in timestamp compat test
ocean e436678
test: Remove debug IO.inspect calls from timestamp compat test
ocean ef97b80
refactor: Improve test reliability and error handling
ocean fffd42e
fix: Add nil-handling to date, time, and bool encode functions
ocean 232a9f6
refactor: Improve test accuracy in type loader/dumper tests
ocean bfb0a79
refactor: Address code review feedback for test quality and documenta…
ocean f363618
Fix datetime_decode to handle timezone-aware ISO8601 strings
ocean 8b1d2de
Add Ecto dumper path tests for nil value encoding
ocean 65dec2b
Update decimal SQL query assertions to accept both numeric and string…
ocean 8ee23c9
Strengthen microsecond preservation tests and fix DB collision issue
ocean 63d3db4
tests: Improve type tests
ocean 81b2cf3
Add microsecond assertions for datetime_usec fields in round-trip test
ocean File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| defmodule EctoLibSql.EctoReturningStructTest do | ||
| use ExUnit.Case, async: false | ||
|
|
||
| defmodule TestRepo do | ||
| use Ecto.Repo, otp_app: :ecto_libsql, adapter: Ecto.Adapters.LibSql | ||
| end | ||
|
|
||
| defmodule User do | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| schema "users" do | ||
| field :name, :string | ||
| field :email, :string | ||
| timestamps() | ||
| end | ||
|
|
||
| def changeset(user, attrs) do | ||
| user | ||
| |> cast(attrs, [:name, :email]) | ||
| |> validate_required([:name, :email]) | ||
| end | ||
| end | ||
|
|
||
| @test_db "z_ecto_libsql_test-ecto_returning.db" | ||
|
|
||
| setup_all do | ||
| {:ok, _} = TestRepo.start_link(database: @test_db) | ||
|
|
||
| # Create table | ||
| Ecto.Adapters.SQL.query!(TestRepo, """ | ||
| CREATE TABLE IF NOT EXISTS users ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| name TEXT NOT NULL, | ||
| email TEXT NOT NULL, | ||
| inserted_at DATETIME, | ||
| updated_at DATETIME | ||
| ) | ||
| """) | ||
|
|
||
| on_exit(fn -> | ||
| EctoLibSql.TestHelpers.cleanup_db_files(@test_db) | ||
| end) | ||
|
|
||
| :ok | ||
| end | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| test "Repo.insert returns populated struct with id and timestamps" do | ||
| changeset = User.changeset(%User{}, %{name: "Alice", email: "alice@example.com"}) | ||
|
|
||
| IO.puts("\n=== Test: INSERT RETURNING via Repo.insert ===") | ||
| result = TestRepo.insert(changeset) | ||
|
|
||
| IO.inspect(result, label: "Insert result") | ||
|
|
||
| case result do | ||
| {:ok, user} -> | ||
| IO.inspect(user, label: "Returned user struct") | ||
|
|
||
| # These assertions should pass if RETURNING struct mapping works | ||
| assert user.id != nil, "❌ FAIL: ID is nil (struct mapping broken)" | ||
| assert is_integer(user.id) and user.id > 0, "ID should be positive integer" | ||
| assert user.name == "Alice", "Name should match" | ||
| assert user.email == "alice@example.com", "Email should match" | ||
| assert user.inserted_at != nil, "❌ FAIL: inserted_at is nil (timestamp conversion broken)" | ||
| assert user.updated_at != nil, "❌ FAIL: updated_at is nil (timestamp conversion broken)" | ||
|
|
||
| IO.puts("✅ PASS: Struct mapping and timestamp conversion working") | ||
| :ok | ||
|
|
||
| {:error, changeset} -> | ||
| IO.inspect(changeset, label: "Error changeset") | ||
| flunk("Insert failed: #{inspect(changeset)}") | ||
| end | ||
| end | ||
|
|
||
| test "Multiple inserts return correctly populated structs" do | ||
| results = | ||
| for i <- 1..3 do | ||
| user_data = %{ | ||
| name: "User#{i}", | ||
| email: "user#{i}@example.com" | ||
| } | ||
|
|
||
| changeset = User.changeset(%User{}, user_data) | ||
| {:ok, user} = TestRepo.insert(changeset) | ||
| user | ||
| end | ||
|
|
||
| assert length(results) == 3 | ||
|
|
||
| Enum.each(results, fn user -> | ||
| assert user.id != nil, "All users should have IDs" | ||
| assert user.inserted_at != nil, "All users should have inserted_at" | ||
| assert user.updated_at != nil, "All users should have updated_at" | ||
| end) | ||
|
|
||
| # IDs should be unique | ||
| ids = Enum.map(results, & &1.id) | ||
| assert length(Enum.uniq(ids)) == 3, "All IDs should be unique" | ||
|
|
||
| IO.puts("✅ PASS: Multiple inserts return populated structs") | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| defmodule EctoLibSql.ReturningTest do | ||
| use ExUnit.Case, async: true | ||
|
|
||
| setup do | ||
| {:ok, conn} = DBConnection.start_link(EctoLibSql, database: ":memory:") | ||
| {:ok, conn: conn} | ||
| end | ||
|
|
||
| test "INSERT RETURNING returns columns and rows", %{conn: conn} do | ||
| # Create table | ||
| {:ok, _, _} = | ||
| DBConnection.execute( | ||
| conn, | ||
| %EctoLibSql.Query{statement: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"}, | ||
| [] | ||
| ) | ||
|
|
||
| # Insert with RETURNING | ||
| query = %EctoLibSql.Query{statement: "INSERT INTO users (name, email) VALUES (?, ?) RETURNING id, name, email"} | ||
| {:ok, _, result} = DBConnection.execute(conn, query, ["Alice", "alice@example.com"]) | ||
|
|
||
| IO.inspect(result, label: "INSERT RETURNING result") | ||
|
|
||
| # Check structure | ||
| assert result.columns != nil, "Columns should not be nil" | ||
| assert result.rows != nil, "Rows should not be nil" | ||
| assert length(result.columns) == 3, "Should have 3 columns" | ||
| assert length(result.rows) == 1, "Should have 1 row" | ||
|
|
||
| # Check values | ||
| [[id, name, email]] = result.rows | ||
| IO.puts("ID: #{inspect(id)}, Name: #{inspect(name)}, Email: #{inspect(email)}") | ||
|
|
||
| assert is_integer(id), "ID should be integer" | ||
| assert id > 0, "ID should be positive" | ||
| assert name == "Alice", "Name should match" | ||
| assert email == "alice@example.com", "Email should match" | ||
| end | ||
|
|
||
| test "INSERT RETURNING with timestamps", %{conn: conn} do | ||
| # Create table with timestamps | ||
| {:ok, _, _} = | ||
| DBConnection.execute( | ||
| conn, | ||
| %EctoLibSql.Query{ | ||
| statement: | ||
| "CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT, inserted_at TEXT, updated_at TEXT)" | ||
| }, | ||
| [] | ||
| ) | ||
|
|
||
| # Insert with RETURNING | ||
| now = DateTime.utc_now() |> DateTime.to_iso8601() | ||
|
|
||
| query = %EctoLibSql.Query{ | ||
| statement: | ||
| "INSERT INTO posts (title, inserted_at, updated_at) VALUES (?, ?, ?) RETURNING id, title, inserted_at, updated_at" | ||
| } | ||
|
|
||
| {:ok, _, result} = DBConnection.execute(conn, query, ["Test Post", now, now]) | ||
|
|
||
| IO.inspect(result, label: "INSERT RETURNING with timestamps") | ||
|
|
||
| assert result.columns == ["id", "title", "inserted_at", "updated_at"] | ||
| [[id, title, inserted_at, updated_at]] = result.rows | ||
|
|
||
| IO.puts("ID: #{inspect(id)}") | ||
| IO.puts("Title: #{inspect(title)}") | ||
| IO.puts("inserted_at: #{inspect(inserted_at)}") | ||
| IO.puts("updated_at: #{inspect(updated_at)}") | ||
|
|
||
| assert is_integer(id) | ||
| assert title == "Test Post" | ||
| assert is_binary(inserted_at) or inserted_at == now | ||
| assert is_binary(updated_at) or updated_at == now | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| defmodule EctoLibSql.TypeCompatibilityTest do | ||
| use ExUnit.Case, async: false | ||
|
|
||
| defmodule TestRepo do | ||
| use Ecto.Repo, otp_app: :ecto_libsql, adapter: Ecto.Adapters.LibSql | ||
| end | ||
|
|
||
| defmodule Record do | ||
| use Ecto.Schema | ||
| import Ecto.Changeset | ||
|
|
||
| schema "records" do | ||
| field :bool_field, :boolean | ||
| field :int_field, :integer | ||
| field :float_field, :float | ||
| field :string_field, :string | ||
| field :map_field, :map | ||
| field :array_field, {:array, :string} | ||
| field :date_field, :date | ||
| field :time_field, :time | ||
| field :utc_datetime_field, :utc_datetime | ||
| field :naive_datetime_field, :naive_datetime | ||
|
|
||
| timestamps() | ||
| end | ||
|
|
||
| def changeset(record, attrs) do | ||
| record | ||
| |> cast(attrs, [ | ||
| :bool_field, :int_field, :float_field, :string_field, | ||
| :map_field, :array_field, :date_field, :time_field, | ||
| :utc_datetime_field, :naive_datetime_field | ||
| ]) | ||
| end | ||
| end | ||
|
|
||
| @test_db "z_ecto_libsql_test-type_compat.db" | ||
|
|
||
| setup_all do | ||
| {:ok, _} = TestRepo.start_link(database: @test_db) | ||
|
|
||
| Ecto.Adapters.SQL.query!(TestRepo, """ | ||
| CREATE TABLE IF NOT EXISTS records ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| bool_field INTEGER, | ||
| int_field INTEGER, | ||
| float_field REAL, | ||
| string_field TEXT, | ||
| map_field TEXT, | ||
| array_field TEXT, | ||
| date_field TEXT, | ||
| time_field TEXT, | ||
| utc_datetime_field TEXT, | ||
| naive_datetime_field TEXT, | ||
| inserted_at DATETIME, | ||
| updated_at DATETIME | ||
| ) | ||
| """) | ||
|
|
||
| on_exit(fn -> | ||
| EctoLibSql.TestHelpers.cleanup_db_files(@test_db) | ||
| end) | ||
|
|
||
| :ok | ||
| end | ||
|
|
||
| test "all field types round-trip correctly" do | ||
| now_utc = DateTime.utc_now() | ||
| now_naive = NaiveDateTime.utc_now() | ||
| today = Date.utc_today() | ||
| current_time = Time.new!(12, 30, 45) | ||
|
|
||
| attrs = %{ | ||
| bool_field: true, | ||
| int_field: 42, | ||
| float_field: 3.14, | ||
| string_field: "test", | ||
| map_field: %{"key" => "value"}, | ||
| array_field: ["a", "b", "c"], | ||
| date_field: today, | ||
| time_field: current_time, | ||
| utc_datetime_field: now_utc, | ||
| naive_datetime_field: now_naive | ||
| } | ||
|
|
||
| # Insert | ||
| changeset = Record.changeset(%Record{}, attrs) | ||
| {:ok, inserted} = TestRepo.insert(changeset) | ||
|
|
||
| IO.puts("\n=== Type Compatibility Test ===") | ||
| IO.inspect(inserted, label: "Inserted record") | ||
|
|
||
| # Verify inserted struct | ||
| assert inserted.id != nil | ||
| assert inserted.bool_field == true | ||
| assert inserted.int_field == 42 | ||
| assert inserted.float_field == 3.14 | ||
| assert inserted.string_field == "test" | ||
| assert inserted.map_field == %{"key" => "value"} | ||
| assert inserted.array_field == ["a", "b", "c"] | ||
| assert inserted.date_field == today | ||
| assert inserted.time_field == current_time | ||
|
|
||
| # Query back | ||
| queried = TestRepo.get(Record, inserted.id) | ||
| IO.inspect(queried, label: "Queried record") | ||
|
|
||
| # Verify queried struct - all types should match | ||
| assert queried.id == inserted.id | ||
| assert queried.bool_field == true, "Boolean should roundtrip" | ||
| assert queried.int_field == 42, "Integer should roundtrip" | ||
| assert queried.float_field == 3.14, "Float should roundtrip" | ||
| assert queried.string_field == "test", "String should roundtrip" | ||
| assert queried.map_field == %{"key" => "value"}, "Map should roundtrip" | ||
| assert queried.array_field == ["a", "b", "c"], "Array should roundtrip" | ||
| assert queried.date_field == today, "Date should roundtrip" | ||
| assert queried.time_field == current_time, "Time should roundtrip" | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| IO.puts("✅ PASS: All types round-trip correctly") | ||
| end | ||
| end | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.