Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/rules/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Rules governing physical project and solution structure (as opposed to logical design, which lives in `design-principles.md`):

- Repo/solution layout: source under `src/<ProjectName>/`, tests under `test/<ProjectName>.UnitTests/` (singular `test`), one solution file (`.slnx`) per repo or example, at its root, referencing every project beneath it.
- Repo/solution layout: source under `src/<ProjectName>/`, unit tests under `test/<ProjectName>.UnitTests/` (singular `test`), integration tests requiring a real dependency (e.g. a database provider) under `test/<ProjectName>.IntegrationTests/`, one solution file (`.slnx`) per repo or example, at its root, referencing every project beneath it.
- Centralize shared MSBuild properties (`TargetFramework`, `Nullable`, `TreatWarningsAsErrors`, etc.) in a `Directory.Build.props` at that same root instead of repeating them per `.csproj`.
- `dotnet pack` runs in CI from day one so packaging bugs surface early.
1 change: 1 addition & 0 deletions .claude/rules/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
- Merge by squash so each feature arrives as a single logical commit on the default branch, consistent with the "one logical change per commit" rule above; the squash commit message keeps the imperative, *why*-focused form.
- Versioning follows semantic versioning: each phase gets its own minor version (Phase 1 → `0.1.x`, Phase 2 → `0.2.x`, …; Phase 6 ships `1.0.0`).
- When a phase completes, tag it on the default branch with an annotated tag (e.g., `git tag -a v0.1.0 -m "..."`) and push the tag to GitHub for reference.
- `PackageVersion` carries a prerelease suffix (`X.Y.0-preview.N`) during a phase's active development. Closing the phase drops the suffix to the clean `X.Y.0` in the same commit that gets tagged, so the tag always matches the package version it marks exactly. The next phase's first commit starts the new prerelease line (`X.(Y+1).0-preview.1`).
- Each roadmap milestone has a matching GitHub milestone (created per phase); the milestone's PR is associated on creation and the milestone closed on merge.
- Update this file when a new convention or correction is established.
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<PropertyGroup Label="Package metadata">
<Authors>Luís Amorim</Authors>
<PackageVersion>0.1.0-preview.1</PackageVersion>
<PackageVersion>0.1.0</PackageVersion>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/lgamorim/sqlbound</PackageProjectUrl>
<RepositoryUrl>https://github.com/lgamorim/sqlbound</RepositoryUrl>
Expand All @@ -29,7 +29,7 @@
as a build-time diagnostic (https://github.com/dotnet/roslyn/issues/41640); only the missing-doc-
comment warning (CS1591) itself is suppressed.
-->
<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('.UnitTests'))">
<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('Tests'))">
<NoWarn>$(NoWarn);CS1591</NoWarn>
<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions sqlbound.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<Project Path="src/SqlBound/SqlBound.csproj" />
</Folder>
<Folder Name="/test/">
<Project Path="test/SqlBound.IntegrationTests/SqlBound.IntegrationTests.csproj" />
<Project Path="test/SqlBound.UnitTests/SqlBound.UnitTests.csproj" />
</Folder>
</Solution>
101 changes: 101 additions & 0 deletions test/SqlBound.IntegrationTests/DapperCoexistenceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Dapper;
using Microsoft.Data.Sqlite;

namespace SqlBound.IntegrationTests;

public class DapperCoexistenceTests
{
[Fact]
public async Task Should_ProduceConsistentResults_When_InterleavingSqlBoundAndDapperOnSameConnection()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync(TestContext.Current.CancellationToken);
var session = new SqlSession(connection);

await session.RunAsync(
"CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
cancellationToken: TestContext.Current.CancellationToken);

await session.RunAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)",
new SqlParameters(("@id", 1), ("@name", "from-sqlbound")),
TestContext.Current.CancellationToken);

var readByDapper = await connection.QuerySingleAsync<string>(
"SELECT name FROM items WHERE id = @id", new { id = 1 });
Assert.Equal("from-sqlbound", readByDapper);

await connection.ExecuteAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)", new { id = 2, name = "from-dapper" });

var readBySqlBound = await session.FetchScalarAsync<string>(
"SELECT name FROM items WHERE id = @id",
new SqlParameters(("@id", 2)),
TestContext.Current.CancellationToken);
Assert.Equal("from-dapper", readBySqlBound);
}

[Fact]
public async Task Should_PersistBothWrites_When_SharedTransactionCommits()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync(TestContext.Current.CancellationToken);
var session = new SqlSession(connection);

await session.RunAsync(
"CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
cancellationToken: TestContext.Current.CancellationToken);

await using (var transaction = await connection.BeginTransactionAsync(TestContext.Current.CancellationToken))
{
var transactionalSession = new SqlSession(connection, transaction);

await transactionalSession.RunAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)",
new SqlParameters(("@id", 1), ("@name", "from-sqlbound")),
TestContext.Current.CancellationToken);

await connection.ExecuteAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)",
new { id = 2, name = "from-dapper" },
transaction);

await transaction.CommitAsync(TestContext.Current.CancellationToken);
}

var count = await connection.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM items");
Assert.Equal(2, count);
}

[Fact]
public async Task Should_DiscardBothWrites_When_SharedTransactionRollsBack()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync(TestContext.Current.CancellationToken);
var session = new SqlSession(connection);

await session.RunAsync(
"CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
cancellationToken: TestContext.Current.CancellationToken);

await using (var transaction = await connection.BeginTransactionAsync(TestContext.Current.CancellationToken))
{
var transactionalSession = new SqlSession(connection, transaction);

await transactionalSession.RunAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)",
new SqlParameters(("@id", 1), ("@name", "from-sqlbound")),
TestContext.Current.CancellationToken);

await connection.ExecuteAsync(
"INSERT INTO items (id, name) VALUES (@id, @name)",
new { id = 2, name = "from-dapper" },
transaction);

await transaction.RollbackAsync(TestContext.Current.CancellationToken);
}

var count = await connection.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM items");
Assert.Equal(0, count);
}
}
32 changes: 32 additions & 0 deletions test/SqlBound.IntegrationTests/SqlBound.IntegrationTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks />
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\SqlBound\SqlBound.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="10.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Dapper" Version="2.1.79" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="3.53.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.v3" Version="3.2.2" />
</ItemGroup>

</Project>
Loading