Files
DorkNet/DorkNet.Server/Data/Entities/TestCaseEntities.cs
Alexa 933c7eb51c feat: close the last route gaps — 100% client route+verb coverage
Every HTTP request the 2023-03-21 client can issue now reaches a handler under
the verb it actually uses: 522/522, with an empty KnownGaps allowlist. The
coverage test asserts it, so a regression fails the build rather than becoming
a 404 in the wild.

The six routes left after the previous pass all needed storage that did not
exist, so they are done here as one schema change rather than stubbed:

- ClubRolePermissionEntity (ClubId, MembershipType, six bools) backs
  PUT club/{id}/permissions/{role}. A club with no stored row falls back to the
  derived default policy, so every club that predates the table behaves exactly
  as before. Partial writes keep the values they currently resolve to, so a
  client that omits a field cannot silently revoke a permission.
- ClubAdditionalImageEntity (ClubId, Slot, ImageName) backs the gallery. The
  details envelope now emits real {ImageName, Slot} rows instead of an empty
  array, and DELETE clears a slot.
- ClubEntity.MinLevel and ClubEntity.ClubChatEnabled back their setters and are
  read back by hasDisabledClubChat and the Club wire shape, which previously
  reported hardcoded defaults.
- ChatMessageEntity.ModerationState backs PUT thread/message/{id}/moderate,
  gated so an author can retract their own message but only club moderators and
  admins can action anyone else's.
- TestCaseEntity.CommentsJson backs the QA comment endpoint, appending rather
  than overwriting so a second tester's note cannot erase the first.

Also corrects the Joinability comment on ClubEntity, which documented
1=RequestToJoin / 2=InviteOnly. The real enum is Open=0, InviteOnly=1,
AskToJoin=2 — the inverted reading the requesttojoin handler was already fixed
away from, left behind where it could mislead the next change.
2026-07-28 09:19:35 +03:00

101 lines
3.6 KiB
C#

using System.ComponentModel.DataAnnotations;
namespace DorkNet.Server.Data.Entities;
/// <summary>
/// QA test case — wire type <c>RecNet.TestCase</c>
/// (<c>Cpp2IL_CS/.../RecNet/TestCase.cs</c>). The 2020 Rec Room
/// client uses this to surface JIRA-tracked test cases in an
/// internal QA tab; users with the developer role claim a case,
/// run it in the named room, then submit a Pass/Fail status.
///
/// Field names + types match the client's
/// <c>TestCase.Deserialize</c> exactly. <see cref="Status"/> is the
/// <c>TestCaseStatus</c> enum: 0=NotYetTested, 1=Claimed, 2=Failed,
/// 3=Passed.
/// </summary>
public class TestCaseEntity
{
/// <summary>EF primary key (long autoincrement) — kept separate
/// from the wire <see cref="Id"/> string so we can index cleanly.</summary>
public long Pk { get; set; }
/// <summary>Wire field <c>Id</c> (string) — typically the JIRA
/// issue key like <c>RR-1234</c>.</summary>
[MaxLength(64)]
public string Id { get; set; } = string.Empty;
[MaxLength(64)]
public string Key { get; set; } = string.Empty;
[MaxLength(256)]
public string Title { get; set; } = string.Empty;
[MaxLength(2000)]
public string Description { get; set; } = string.Empty;
[MaxLength(128)]
public string RoomName { get; set; } = string.Empty;
/// <summary>TestCaseStatus enum: 0=NotYetTested, 1=Claimed,
/// 2=Failed, 3=Passed.</summary>
public int Status { get; set; } = 0;
public int MinNumAssignedPlayers { get; set; } = 1;
/// <summary>JSON list of player ids currently claimed on this
/// test case. Mirrors wire <c>AssignedPlayerIds</c> (List&lt;int&gt;).</summary>
public string AssignedPlayerIdsJson { get; set; } = "[]";
/// <summary>Sibling JSON list of names for display (kept in sync
/// with <see cref="AssignedPlayerIdsJson"/>).</summary>
public string AssignedPlayerNamesJson { get; set; } = "[]";
/// <summary>JSON list of tag strings.</summary>
public string TagsJson { get; set; } = "[]";
[MaxLength(512)]
public string JiraUrl { get; set; } = string.Empty;
[MaxLength(512)]
public string JiraBugUrl { get; set; } = string.Empty;
/// <summary>Optional FK to the <see cref="TestPassEntity"/> this
/// case belongs to (null = orphan / standalone).</summary>
public uint? TestPassId { get; set; }
/// <summary>Append-only JSON array of QA comments posted through
/// <c>POST api/testcasemanagement/v1/testcase/{id}/comment</c>.</summary>
public string CommentsJson { get; set; } = "[]";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
/// <summary>Wire type <c>RecNet.TestPass</c> — a versioned bundle of
/// test cases the QA team groups for a release pass. Listed via
/// <c>GET api/testcasemanagement/v1/testpasssummary</c>; one fetched
/// in detail via <c>GET api/testcasemanagement/v1/testpass/{id}</c>.
/// </summary>
public class TestPassEntity
{
/// <summary>Wire <c>Id</c> is uint — store as long PK and cast.</summary>
public uint Id { get; set; }
[MaxLength(128)]
public string Name { get; set; } = string.Empty;
[MaxLength(2000)]
public string Description { get; set; } = string.Empty;
public DateTime StartDate { get; set; } = DateTime.UtcNow;
/// <summary>Null while the pass is still active.</summary>
public DateTime? EndDate { get; set; }
public bool WasManuallyClosed { get; set; } = false;
/// <summary>JSON list of tag strings.</summary>
public string TagsJson { get; set; } = "[]";
}