Files
DorkNet/DorkNet.Server/Migrations/20260727200252_Client2023WireFixes.cs
Alexa 936aa061c0 test: guard both schema-delivery paths; fix playlist backfill defaults
Adds SchemaBootstrapParityTests, which closes the hole that let the
missing-column outage ship green. Every other test runs against a FRESH
database where EnsureCreated builds the whole model from the entities, so a
column that only exists in the model always appears — the failure mode is
invisible by construction. These two tests model an EXISTING database instead:

- Every model table/column must be reachable via a committed migration OR an
  idempotent Ensure* patch. Something covered by neither reaches only brand-new
  databases, and every query touching it throws on a deployed server.
- Anything added after the Initial migration must additionally appear as a
  Postgres ALTER/CREATE in DatabaseBootstrap, because that path is
  EnsureCreated-only and never replays migrations. This is precisely the rule
  PrivateInstances.RoomCode, Clubs.ClubChatEnabled and
  ChatMessages.ModerationState broke.

The first test asserts the union rather than migrations alone: a large amount of
schema legitimately predates the rule and lives only in the helpers. Tightening
that is separate cleanup; what must never happen is a column covered by nothing.

The helper-coverage parser reads the Ensure* SQL out of the source rather than
executing the private methods, and handles both C# string styles the file mixes
plus the loops that interpolate a column name (EnsureGameRewardColumnsAsync).

Also fixes a real data bug found in review: the Client2023WireFixes migration
backfilled SupportsJuniors/SupportsScreens/SupportsTeleportVR/SupportsWalkVR to
false for existing rows, while the entity default is true. A C# property
initializer is not a database default, so EF emitted false and every playlist
predating the migration would have been silently marked as not supporting
juniors, screens or either VR movement mode — the opposite of intent, and
inconsistent with the bootstrap helper, which already used true.
2026-07-28 09:42:48 +03:00

133 lines
4.3 KiB
C#

using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DorkNet.Server.Migrations
{
/// <summary>
/// Columns needed by the March-2023 client wire fixes: the playlist
/// settings the client can set (accessibility, level voting, VR/junior
/// restrictions, content warning) which used to be acknowledged and thrown
/// away, plus a host-chosen room code on private instances.
///
/// Hand-trimmed after generation: `migrations add` diffs against the
/// committed model snapshot, which already lagged the entity model, so the
/// generated file also carried unrelated pre-existing drift
/// (ServerSettings, Rooms, Avatars, ...). Replaying that against a database
/// where those columns already exist fails on the first duplicate
/// ADD COLUMN, so only the columns this change introduces are kept.
/// </summary>
public partial class Client2023WireFixes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "RoomCode",
table: "PrivateInstances",
type: "TEXT",
maxLength: 16,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<int>(
name: "Accessibility",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "CustomWarning",
table: "Playlists",
type: "TEXT",
maxLength: 512,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<bool>(
name: "SupportsJuniors",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: true);
migrationBuilder.AddColumn<bool>(
name: "SupportsLevelVoting",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "SupportsScreens",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: true);
migrationBuilder.AddColumn<bool>(
name: "SupportsTeleportVR",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: true);
migrationBuilder.AddColumn<bool>(
name: "SupportsWalkVR",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: true);
migrationBuilder.AddColumn<int>(
name: "WarningMask",
table: "Playlists",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "RoomCode",
table: "PrivateInstances");
migrationBuilder.DropColumn(
name: "Accessibility",
table: "Playlists");
migrationBuilder.DropColumn(
name: "CustomWarning",
table: "Playlists");
migrationBuilder.DropColumn(
name: "SupportsJuniors",
table: "Playlists");
migrationBuilder.DropColumn(
name: "SupportsLevelVoting",
table: "Playlists");
migrationBuilder.DropColumn(
name: "SupportsScreens",
table: "Playlists");
migrationBuilder.DropColumn(
name: "SupportsTeleportVR",
table: "Playlists");
migrationBuilder.DropColumn(
name: "SupportsWalkVR",
table: "Playlists");
migrationBuilder.DropColumn(
name: "WarningMask",
table: "Playlists");
}
}
}