forked from xiongyuxing/tiku-backend.net
feat: add vocabulary and handbook persistence
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.QuestionBanks;
|
||||
@@ -22,13 +23,22 @@ public sealed class PersistenceModelTests
|
||||
.Select(entity => entity.GetTableName())
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(28, tableNames.Count);
|
||||
Assert.Equal(37, tableNames.Count);
|
||||
Assert.Contains("tenants", tableNames);
|
||||
Assert.Contains("tenant_settings", tableNames);
|
||||
Assert.Contains("content_entries", tableNames);
|
||||
Assert.Contains("content_nodes", tableNames);
|
||||
Assert.Contains("question_collections", tableNames);
|
||||
Assert.Contains("practice_blueprints", tableNames);
|
||||
Assert.Contains("vocabulary_units", tableNames);
|
||||
Assert.Contains("vocabulary_words", tableNames);
|
||||
Assert.Contains("user_word_progress", tableNames);
|
||||
Assert.Contains("user_word_favorites", tableNames);
|
||||
Assert.Contains("handbook_subjects", tableNames);
|
||||
Assert.Contains("handbook_chapters", tableNames);
|
||||
Assert.Contains("handbook_entries", tableNames);
|
||||
Assert.Contains("question_type_groups", tableNames);
|
||||
Assert.Contains("subject_shares", tableNames);
|
||||
Assert.Contains("questions", tableNames);
|
||||
Assert.Contains("question_versions", tableNames);
|
||||
Assert.Contains("answer_records", tableNames);
|
||||
@@ -99,4 +109,104 @@ public sealed class PersistenceModelTests
|
||||
properties => properties.SequenceEqual(
|
||||
["TenantId", "Id", "CurrentVersionId"]));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(VocabularyWord), nameof(VocabularyWord.Tags), "'[]'::jsonb")]
|
||||
[InlineData(typeof(VocabularyWord), nameof(VocabularyWord.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(HandbookSubject), nameof(HandbookSubject.MajorLegacyIds), "'[]'::jsonb")]
|
||||
[InlineData(typeof(HandbookEntry), nameof(HandbookEntry.Tags), "'[]'::jsonb")]
|
||||
[InlineData(typeof(QuestionTypeGroup), nameof(QuestionTypeGroup.Types), "'[]'::jsonb")]
|
||||
[InlineData(typeof(UserWordProgress), nameof(UserWordProgress.Metadata), "'{}'::jsonb")]
|
||||
public void Study_content_json_properties_are_mapped_to_jsonb(
|
||||
Type entityType,
|
||||
string propertyName,
|
||||
string expectedDefaultValueSql)
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
var property = context.Model.FindEntityType(entityType)!
|
||||
.FindProperty(propertyName)!;
|
||||
|
||||
Assert.Equal("jsonb", property.GetColumnType());
|
||||
Assert.Equal(expectedDefaultValueSql, property.GetDefaultValueSql());
|
||||
Assert.Equal(typeof(System.Text.Json.JsonElement), property.ClrType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void User_word_progress_has_review_constraints_and_precision()
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
var entityType = context.GetService<IDesignTimeModel>()
|
||||
.Model
|
||||
.FindEntityType(typeof(UserWordProgress))!;
|
||||
var checkConstraints = entityType.GetCheckConstraints()
|
||||
.Select(constraint => constraint.Name)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
var easeFactor = entityType.FindProperty(nameof(UserWordProgress.EaseFactor))!;
|
||||
Assert.Equal(4, easeFactor.GetPrecision());
|
||||
Assert.Equal(2, easeFactor.GetScale());
|
||||
Assert.Contains("ck_user_word_progress_review_count", checkConstraints);
|
||||
Assert.Contains("ck_user_word_progress_correct_streak", checkConstraints);
|
||||
Assert.Contains("ck_user_word_progress_ease_factor", checkConstraints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Study_content_relations_include_tenant_in_foreign_keys()
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
var entityTypes = new[]
|
||||
{
|
||||
typeof(VocabularyUnit),
|
||||
typeof(VocabularyWord),
|
||||
typeof(UserWordProgress),
|
||||
typeof(UserWordFavorite),
|
||||
typeof(HandbookSubject),
|
||||
typeof(HandbookChapter),
|
||||
typeof(HandbookEntry),
|
||||
typeof(QuestionTypeGroup),
|
||||
typeof(SubjectShare)
|
||||
};
|
||||
|
||||
var compositeForeignKeys = entityTypes
|
||||
.SelectMany(entityType => context.Model.FindEntityType(entityType)!
|
||||
.GetForeignKeys())
|
||||
.Where(foreignKey => foreignKey.Properties.Count > 1)
|
||||
.Select(foreignKey => foreignKey.Properties
|
||||
.Select(property => property.Name)
|
||||
.ToArray());
|
||||
|
||||
Assert.All(
|
||||
compositeForeignKeys,
|
||||
properties => Assert.Contains("TenantId", properties));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Word_user_join_tables_have_tenant_scoped_unique_indexes()
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
AssertHasUniqueIndex<UserWordProgress>(
|
||||
nameof(UserWordProgress.TenantId),
|
||||
nameof(UserWordProgress.UserId),
|
||||
nameof(UserWordProgress.WordId));
|
||||
AssertHasUniqueIndex<UserWordFavorite>(
|
||||
nameof(UserWordFavorite.TenantId),
|
||||
nameof(UserWordFavorite.UserId),
|
||||
nameof(UserWordFavorite.WordId));
|
||||
|
||||
void AssertHasUniqueIndex<TEntity>(params string[] propertyNames)
|
||||
{
|
||||
var indexes = context.Model.FindEntityType(typeof(TEntity))!.GetIndexes();
|
||||
|
||||
Assert.Contains(
|
||||
indexes,
|
||||
index => index.IsUnique
|
||||
&& index.Properties
|
||||
.Select(property => property.Name)
|
||||
.SequenceEqual(propertyNames));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user