feat(admission): add scheduled group access
CI / validate (push) Successful in 6m15s
Release / release (push) Successful in 7m49s

This commit is contained in:
dmg
2026-08-02 14:04:48 -04:00
parent eb1c5b6de4
commit c131465ff5
32 changed files with 2446 additions and 59 deletions
+31 -14
View File
@@ -1,33 +1,50 @@
import { describe, expect, it } from "vitest";
import { admissionDenialMessage, DEFAULT_ADMISSION_MESSAGES, parseAdmissionMessages } from "./admission-settings";
import { admissionDenialMessage, DEFAULT_ADMISSION_MESSAGES, parseAdmissionMessages, renderAdmissionMessage } from "./admission-settings";
describe("admission message settings", () => {
it("normalizes three independently configured denial messages", () => {
const formData = new FormData();
formData.set("registrationMessage", " Register your account before joining. ");
formData.set("groupAccessDeniedMessage", "This group cannot access the server right now.");
formData.set("vpnDeniedMessage", "VPN access requires a host-approved exception.");
it("normalizes independently configured denial templates with allowed variables", () => {
const formData = validMessages();
formData.set("registrationMessage", " Register {player} before joining {group}. ");
formData.set("scheduledAccessDeniedMessage", "{player}, {group} may join from {next_start} to {next_end}.");
expect(parseAdmissionMessages(formData)).toEqual({
registrationMessage: "Register your account before joining.",
groupAccessDeniedMessage: "This group cannot access the server right now.",
vpnDeniedMessage: "VPN access requires a host-approved exception.",
registrationMessage: "Register {player} before joining {group}.",
groupAccessDeniedMessage: "{player} cannot access the server with {group}.",
vpnDeniedMessage: "VPN access for {player} in {group} requires an exception.",
scheduledAccessDeniedMessage: "{player}, {group} may join from {next_start} to {next_end}.",
});
});
it("selects the configured message for each admission denial reason", () => {
expect(admissionDenialMessage("not_registered", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.registrationMessage);
expect(admissionDenialMessage("group_access_disabled", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.groupAccessDeniedMessage);
expect(admissionDenialMessage("schedule_disallowed", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.scheduledAccessDeniedMessage);
expect(admissionDenialMessage("anonymized_network_disallowed", DEFAULT_ADMISSION_MESSAGES)).toBe(DEFAULT_ADMISSION_MESSAGES.vpnDeniedMessage);
});
it("rejects missing, short, overlong, or control-character messages", () => {
for (const invalid of ["short", "a".repeat(501), "Denied\nInjected"] as const) {
const formData = new FormData();
formData.set("registrationMessage", DEFAULT_ADMISSION_MESSAGES.registrationMessage);
formData.set("groupAccessDeniedMessage", DEFAULT_ADMISSION_MESSAGES.groupAccessDeniedMessage);
it("renders static variables without evaluating expressions", () => {
expect(renderAdmissionMessage("{player} uses {group}; next: {next_start}{next_end}.", {
player: "AlexMC",
group: "Friday friends",
next_start: "2026-08-07 20:00 UTC",
next_end: "2026-08-07 23:59 UTC",
})).toBe("AlexMC uses Friday friends; next: 2026-08-07 20:00 UTC2026-08-07 23:59 UTC.");
});
it("rejects missing, short, overlong, control-character, or unsupported-variable messages", () => {
for (const invalid of ["short", "a".repeat(501), "Denied\nInjected", "Denied for {next_start}.", "Denied for {unknown}."] as const) {
const formData = validMessages();
formData.set("vpnDeniedMessage", invalid);
expect(parseAdmissionMessages(formData)).toBeNull();
}
});
});
function validMessages() {
const formData = new FormData();
formData.set("registrationMessage", "Register {player} before joining {group}.");
formData.set("groupAccessDeniedMessage", "{player} cannot access the server with {group}.");
formData.set("vpnDeniedMessage", "VPN access for {player} in {group} requires an exception.");
formData.set("scheduledAccessDeniedMessage", "{group} may join from {next_start} to {next_end}.");
return formData;
}