Skip to content

Commit f16b1f2

Browse files
test: Add tests for idempotent mode
1 parent 7660d2b commit f16b1f2

1 file changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using CmdScale.EntityFrameworkCore.TimescaleDB.Operations;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.Infrastructure;
4+
using Microsoft.EntityFrameworkCore.Migrations;
5+
using Microsoft.EntityFrameworkCore.Migrations.Operations;
6+
7+
namespace CmdScale.EntityFrameworkCore.TimescaleDB.Tests.Generators;
8+
9+
public class TimescaleDbMigrationsSqlGeneratorTests
10+
{
11+
private class TestContext : DbContext
12+
{
13+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
14+
=> optionsBuilder.UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
15+
.UseTimescaleDb();
16+
}
17+
18+
private static string GenerateSql(
19+
List<MigrationOperation> operations,
20+
MigrationsSqlGenerationOptions? options = null)
21+
{
22+
using TestContext context = new();
23+
IMigrationsSqlGenerator sqlGenerator = context.GetService<IMigrationsSqlGenerator>();
24+
25+
IReadOnlyList<MigrationCommand> commands = options.HasValue
26+
? sqlGenerator.Generate(operations, context.Model, options.Value)
27+
: sqlGenerator.Generate(operations, context.Model);
28+
29+
return string.Join("\n", commands.Select(c => c.CommandText));
30+
}
31+
32+
#region Should_Use_Perform_For_CreateHypertable_In_Idempotent_Mode
33+
34+
[Fact]
35+
public void Should_Use_Perform_For_CreateHypertable_In_Idempotent_Mode()
36+
{
37+
// Arrange
38+
CreateHypertableOperation operation = new()
39+
{
40+
TableName = "IdempotentHypertable",
41+
Schema = "public",
42+
TimeColumnName = "time"
43+
};
44+
List<MigrationOperation> operations = [operation];
45+
46+
// Act
47+
string sql = GenerateSql(operations, MigrationsSqlGenerationOptions.Idempotent | MigrationsSqlGenerationOptions.Script);
48+
49+
// Assert
50+
Assert.Contains("PERFORM create_hypertable", sql);
51+
Assert.DoesNotContain("SELECT create_hypertable", sql);
52+
}
53+
54+
#endregion
55+
56+
#region Should_Use_Select_For_CreateHypertable_In_NonIdempotent_Mode
57+
58+
[Fact]
59+
public void Should_Use_Select_For_CreateHypertable_In_NonIdempotent_Mode()
60+
{
61+
// Arrange
62+
CreateHypertableOperation operation = new()
63+
{
64+
TableName = "NonIdempotentHypertable",
65+
Schema = "public",
66+
TimeColumnName = "time"
67+
};
68+
List<MigrationOperation> operations = [operation];
69+
70+
// Act
71+
string sql = GenerateSql(operations);
72+
73+
// Assert
74+
Assert.Contains("SELECT create_hypertable", sql);
75+
Assert.DoesNotContain("PERFORM", sql);
76+
}
77+
78+
#endregion
79+
80+
#region Should_Use_Perform_For_SqlOperation_In_Idempotent_Mode
81+
82+
[Fact]
83+
public void Should_Use_Perform_For_SqlOperation_In_Idempotent_Mode()
84+
{
85+
// Arrange
86+
SqlOperation operation = new()
87+
{
88+
Sql = "SELECT add_retention_policy('public.\"TestTable\"', drop_after => INTERVAL '30 days');"
89+
};
90+
List<MigrationOperation> operations = [operation];
91+
92+
// Act
93+
string sql = GenerateSql(operations, MigrationsSqlGenerationOptions.Idempotent | MigrationsSqlGenerationOptions.Script);
94+
95+
// Assert
96+
Assert.Contains("PERFORM add_retention_policy", sql);
97+
Assert.DoesNotContain("SELECT add_retention", sql);
98+
}
99+
100+
#endregion
101+
102+
#region Should_Preserve_Select_For_DDL_SqlOperation_In_Idempotent_Mode
103+
104+
[Fact]
105+
public void Should_Preserve_Select_For_DDL_SqlOperation_In_Idempotent_Mode()
106+
{
107+
// Arrange
108+
SqlOperation operation = new()
109+
{
110+
Sql = "CREATE MATERIALIZED VIEW daily_agg AS\nSELECT time_bucket('1 day', time) AS bucket, avg(value) FROM metrics GROUP BY bucket;"
111+
};
112+
List<MigrationOperation> operations = [operation];
113+
114+
// Act
115+
string sql = GenerateSql(operations, MigrationsSqlGenerationOptions.Idempotent | MigrationsSqlGenerationOptions.Script);
116+
117+
// Assert — DDL statement passes through unchanged; SELECT inside CREATE ... AS SELECT must not be replaced
118+
Assert.Contains("SELECT", sql);
119+
Assert.Contains("CREATE MATERIALIZED VIEW", sql);
120+
}
121+
122+
#endregion
123+
124+
#region Should_Use_Select_For_SqlOperation_In_NonIdempotent_Mode
125+
126+
[Fact]
127+
public void Should_Use_Select_For_SqlOperation_In_NonIdempotent_Mode()
128+
{
129+
// Arrange
130+
SqlOperation operation = new()
131+
{
132+
Sql = "SELECT add_retention_policy('public.\"TestTable\"', drop_after => INTERVAL '30 days');"
133+
};
134+
List<MigrationOperation> operations = [operation];
135+
136+
// Act
137+
string sql = GenerateSql(operations);
138+
139+
// Assert
140+
Assert.Contains("SELECT add_retention_policy", sql);
141+
Assert.DoesNotContain("PERFORM", sql);
142+
}
143+
144+
#endregion
145+
}

0 commit comments

Comments
 (0)