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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
![CmdScale Project](https://github.com/cmdscale/.github/raw/main/profile/assets/CmdShield.svg)
[![Test Workflow](https://github.com/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/actions/workflows/run-tests.yml/badge.svg)](https://github.com/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/actions/workflows/run-tests.yml)
[![codecov](https://codecov.io/gh/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/graph/badge.svg?token=YP3YCJLQ41)](https://codecov.io/gh/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB)
[![GitHub release (latest by date)](https://img.shields.io/github/v/tag/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB)](https://github.com/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/tags)
[![GitHub issues](https://img.shields.io/github/issues/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB)](https://github.com/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/issues)
[![GitHub license](https://img.shields.io/github/license/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB)](https://github.com/cmdscale/CmdScale.EntityFrameworkCore.TimescaleDB/blob/main/LICENSE)
Expand Down Expand Up @@ -176,14 +177,14 @@ Generate an HTML coverage report using [ReportGenerator](https://github.com/dani
# Install ReportGenerator (once)
dotnet tool install -g dotnet-reportgenerator-globaltool

# Run tests with coverage collection (output to ./TestResults)
dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults
# Run tests with coverage collection
dotnet test tests/Eftdb.Tests --settings tests/Eftdb.Tests/coverlet.runsettings --collect:"XPlat Code Coverage"

# Generate HTML report from coverage files
reportgenerator -reports:"TestResults/**/coverage.cobertura.xml" -targetdir:"TestResults/CoverageReport" -reporttypes:Html
reportgenerator -reports:"tests/Eftdb.Tests/TestResults/**/coverage.cobertura.xml" -targetdir:"tests/Eftdb.Tests/TestResults/CoverageReport" -reporttypes:Html
```

The HTML report will be generated at `TestResults/CoverageReport/index.html`.
The HTML report will be generated at `tests/Eftdb.Tests/TestResults/CoverageReport/index.html`.

### Mutation Testing

Expand Down
30 changes: 1 addition & 29 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,8 @@ coverage:
target: auto
threshold: 2%

component_management:
individual_components:
- component_id: eftdb
name: CmdScale.EntityFrameworkCore.TimescaleDB
paths:
- src/CmdScale.EntityFrameworkCore.TimescaleDB/**
- component_id: eftdb-design
name: CmdScale.EntityFrameworkCore.TimescaleDB.Design
paths:
- src/CmdScale.EntityFrameworkCore.TimescaleDB.Design/**

flag_management:
individual_flags:
- name: eftdb
paths:
- src/CmdScale.EntityFrameworkCore.TimescaleDB/**
carryforward: true
statuses:
- type: project
target: 80%
- name: eftdb-design
paths:
- src/CmdScale.EntityFrameworkCore.TimescaleDB.Design/**
carryforward: true
statuses:
- type: project
target: 80%

comment:
layout: "header, diff, flags, components, files"
layout: "header, diff, files"
behavior: default
require_changes: true

Expand Down
1 change: 1 addition & 0 deletions src/Eftdb/Internals/WhereClauseEpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace CmdScale.EntityFrameworkCore.TimescaleDB.Internals
{

// TODO: This is not in use, yet and will need more work to be functional. Therefore, this class will be ignored by code coverage tools. Don't forget to remove the exclusion when you start using it.
/// <summary>
/// A simplified visitor to translate a WHERE clause LambdaExpression into a SQL string.
/// This must be used by your IMigrationsModelDiffer, not the SQL generator.
Expand Down
208 changes: 208 additions & 0 deletions tests/Eftdb.Tests/Differs/ContinuousAggregateDifferTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1880,4 +1880,212 @@ public void Should_Handle_Both_Null_Models()
}

#endregion

#region Should_Drop_And_Recreate_When_AggregateFunctions_Count_Differs

private class MetricEntity22
{
public DateTime Timestamp { get; set; }
public double Value { get; set; }
}

private class MetricAggregate22
{
public DateTime TimeBucket { get; set; }
public double AvgValue { get; set; }
}

private class MetricAggregateMultiple22
{
public DateTime TimeBucket { get; set; }
public double AvgValue { get; set; }
public double MaxValue { get; set; }
}

private class SingleAggregateFunctionContext22 : DbContext
{
public DbSet<MetricEntity22> Metrics => Set<MetricEntity22>();
public DbSet<MetricAggregate22> HourlyMetrics => Set<MetricAggregate22>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
.UseTimescaleDb();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MetricEntity22>(entity =>
{
entity.ToTable("Metrics");
entity.HasNoKey();
entity.IsHypertable(x => x.Timestamp);
});

modelBuilder.Entity<MetricAggregate22>(entity =>
{
entity.HasNoKey();
entity.IsContinuousAggregate<MetricAggregate22, MetricEntity22>(
"hourly_metrics",
"1 hour",
x => x.Timestamp)
.AddAggregateFunction(x => x.AvgValue, x => x.Value, EAggregateFunction.Avg);
});
}
}

private class MultipleAggregateFunctionsContext22 : DbContext
{
public DbSet<MetricEntity22> Metrics => Set<MetricEntity22>();
public DbSet<MetricAggregateMultiple22> HourlyMetrics => Set<MetricAggregateMultiple22>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
.UseTimescaleDb();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MetricEntity22>(entity =>
{
entity.ToTable("Metrics");
entity.HasNoKey();
entity.IsHypertable(x => x.Timestamp);
});

modelBuilder.Entity<MetricAggregateMultiple22>(entity =>
{
entity.HasNoKey();
entity.IsContinuousAggregate<MetricAggregateMultiple22, MetricEntity22>(
"hourly_metrics",
"1 hour",
x => x.Timestamp)
.AddAggregateFunction(x => x.AvgValue, x => x.Value, EAggregateFunction.Avg)
.AddAggregateFunction(x => x.MaxValue, x => x.Value, EAggregateFunction.Max);
});
}
}

[Fact]
public void Should_Drop_And_Recreate_When_AggregateFunctions_Count_Differs()
{
using SingleAggregateFunctionContext22 sourceContext = new();
using MultipleAggregateFunctionsContext22 targetContext = new();

IRelationalModel sourceModel = GetModel(sourceContext);
IRelationalModel targetModel = GetModel(targetContext);

ContinuousAggregateDiffer differ = new();

IReadOnlyList<MigrationOperation> operations = differ.GetDifferences(sourceModel, targetModel);

Assert.Contains(operations, op => op is DropContinuousAggregateOperation);
Assert.Contains(operations, op => op is CreateContinuousAggregateOperation);
}

#endregion

#region Should_Drop_And_Recreate_When_GroupByColumns_Count_Differs

private class MetricEntity23
{
public DateTime Timestamp { get; set; }
public double Value { get; set; }
public string? Category { get; set; }
public string? Region { get; set; }
}

private class MetricAggregateSingleGroupBy23
{
public DateTime TimeBucket { get; set; }
public double AvgValue { get; set; }
public string? Category { get; set; }
}

private class MetricAggregateMultipleGroupBy23
{
public DateTime TimeBucket { get; set; }
public double AvgValue { get; set; }
public string? Category { get; set; }
public string? Region { get; set; }
}

private class SingleGroupByColumnContext23 : DbContext
{
public DbSet<MetricEntity23> Metrics => Set<MetricEntity23>();
public DbSet<MetricAggregateSingleGroupBy23> HourlyMetrics => Set<MetricAggregateSingleGroupBy23>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
.UseTimescaleDb();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MetricEntity23>(entity =>
{
entity.ToTable("Metrics");
entity.HasNoKey();
entity.IsHypertable(x => x.Timestamp);
});

modelBuilder.Entity<MetricAggregateSingleGroupBy23>(entity =>
{
entity.HasNoKey();
entity.IsContinuousAggregate<MetricAggregateSingleGroupBy23, MetricEntity23>(
"hourly_metrics",
"1 hour",
x => x.Timestamp)
.AddAggregateFunction(x => x.AvgValue, x => x.Value, EAggregateFunction.Avg)
.AddGroupByColumn(x => x.Category);
});
}
}

private class MultipleGroupByColumnsContext23 : DbContext
{
public DbSet<MetricEntity23> Metrics => Set<MetricEntity23>();
public DbSet<MetricAggregateMultipleGroupBy23> HourlyMetrics => Set<MetricAggregateMultipleGroupBy23>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
.UseTimescaleDb();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MetricEntity23>(entity =>
{
entity.ToTable("Metrics");
entity.HasNoKey();
entity.IsHypertable(x => x.Timestamp);
});

modelBuilder.Entity<MetricAggregateMultipleGroupBy23>(entity =>
{
entity.HasNoKey();
entity.IsContinuousAggregate<MetricAggregateMultipleGroupBy23, MetricEntity23>(
"hourly_metrics",
"1 hour",
x => x.Timestamp)
.AddAggregateFunction(x => x.AvgValue, x => x.Value, EAggregateFunction.Avg)
.AddGroupByColumn(x => x.Category)
.AddGroupByColumn(x => x.Region);
});
}
}

[Fact]
public void Should_Drop_And_Recreate_When_GroupByColumns_Count_Differs()
{
using SingleGroupByColumnContext23 sourceContext = new();
using MultipleGroupByColumnsContext23 targetContext = new();

IRelationalModel sourceModel = GetModel(sourceContext);
IRelationalModel targetModel = GetModel(targetContext);

ContinuousAggregateDiffer differ = new();

IReadOnlyList<MigrationOperation> operations = differ.GetDifferences(sourceModel, targetModel);

Assert.Contains(operations, op => op is DropContinuousAggregateOperation);
Assert.Contains(operations, op => op is CreateContinuousAggregateOperation);
}

#endregion
}
Loading