using System.Collections.Concurrent; using SimpleAdmin.Core; using SimpleAdmin.Application; using SqlSugar; var passed = 0; void Check(bool condition, string name) { if (!condition) throw new Exception("FAIL: " + name); passed++; Console.WriteLine("PASS: " + name); } void Reject(Action action, string name) { try { action(); } catch (InvalidOperationException) { Check(true, name); return; } throw new Exception("FAIL: expected rejection: " + name); } // Uses the real BasePageInput and real ORM SQL generation; never opens a DB connection. using var db = new SqlSugarClient(new ConnectionConfig { ConnectionString = "Server=127.0.0.1;Database=never_connected;Uid=unused;Pwd=unused;", DbType = SqlSugar.DbType.MySql, IsAutoCloseConnection = true }); foreach (var order in new[] { "asc, CASE WHEN 1=1 THEN 0 ELSE 1 END", "desc;DROP TABLE x", "asc--", "asc\tLIMIT 1", "random()" }) Reject(() => new BasePageInput { SortField = "Id", SortOrder = order }.GetSafeOrderBy("Id"), "reject sort direction: " + order); foreach (var field in new[] { "Id,(CASE\tWHEN\t1=1\tTHEN\t0\tELSE\t1\tEND)", "Id\tDESC", "Id/**/", "Id;SELECT", "Id\n", "u.Id", "`Id`", "Id,Name", new string('a', 65) }) Reject(() => new BasePageInput { SortField = field }.GetSafeOrderBy("Id"), "reject sort field: " + field.Replace("\n", "")); Reject(() => new BasePageInput { SortField = "NotAllowed" }.GetSafeOrderBy("Id"), "column whitelist enforced"); foreach (var sample in new[] { ("ascend", "asc"), ("descend", "desc"), (" ASC ", "asc"), ("", "desc"), ((string)null, "desc") }) { var input = new BasePageInput { SortField = "id", SortOrder = sample.Item1 }; var orderBy = input.GetSafeOrderBy("Id", "Name"); Check(orderBy == "Id " + sample.Item2, "normalizes direction and maps column: " + sample.Item1); var sql = db.Queryable().OrderBy(orderBy).ToSql().Key; Check(sql.Contains("ORDER BY", StringComparison.OrdinalIgnoreCase) && !sql.Contains("CASE"), "safe ORM SQL generated"); } Check(new BasePageInput().GetSafeOrderBy("Id") == "", "no requested sort preserves default"); // Uses the real state guard and production status constants. Check(OnboardingSignStateGuard.Resolve(ElectronicSignStatus.PendingEmployeeSign, ElectronicSignStatus.PendingCompanySeal) == ElectronicSignStatus.PendingCompanySeal, "employee signed -> company seal"); Check(OnboardingSignStateGuard.Resolve(ElectronicSignStatus.PendingCompanySeal, ElectronicSignStatus.Completed) == ElectronicSignStatus.Completed, "company seal -> completed"); Check(OnboardingSignStateGuard.Resolve(ElectronicSignStatus.Archived, ElectronicSignStatus.Completed) == ElectronicSignStatus.Archived, "completed provider result preserves local archive"); foreach (var terminal in new[] { ElectronicSignStatus.Completed, ElectronicSignStatus.Archived, ElectronicSignStatus.Invalid, ElectronicSignStatus.Expired, ElectronicSignStatus.Rejected }) { Check(OnboardingSignStateGuard.Resolve(terminal, terminal) == terminal, "terminal replay allowed: " + terminal); Reject(() => OnboardingSignStateGuard.Resolve(terminal, ElectronicSignStatus.PendingEmployeeSign), "terminal rollback rejected: " + terminal); } foreach (var backward in new[] { ElectronicSignStatus.PendingEmployeeSign, ElectronicSignStatus.Draft, ElectronicSignStatus.Creating, ElectronicSignStatus.Failed }) Reject(() => OnboardingSignStateGuard.Resolve(ElectronicSignStatus.PendingCompanySeal, backward), "signed progress cannot regress: " + backward); // Exercises the real async gate with a fake external lease. Does NOT claim MySQL GET_LOCK integration. var leases = new FakeLeases(); Task Gate(string key, Func> action) => OnboardingOperationGate.Run(key, () => leases.Acquire(key), action); var entered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var release = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var first = Gate("task:1", async () => { entered.SetResult(); await release.Task; return 1; }); await entered.Task; var contenders = Enumerable.Range(0, 64).Select(async _ => { try { await Gate("task:1", () => Task.FromResult(2)); return false; } catch (InvalidOperationException) { return true; } }).ToArray(); Check((await Task.WhenAll(contenders)).All(x => x), "64 independent same-task operations rejected while held"); Check(await Gate("task:2", () => Task.FromResult(7)) == 7, "different tasks proceed independently"); release.SetResult(); await first; Check(await Gate("task:1", () => Task.FromResult(3)) == 3, "lock released after completion"); var before = leases.AcquisitionCount; Check(await Gate("nested", () => Gate("nested", () => Gate("identity", () => Gate("nested", () => Task.FromResult(9))))) == 9, "nested task/snapshot call is reentrant"); Check(leases.AcquisitionCount - before == 2, "reentrant gate acquires each resource only once"); try { await Gate("failure", () => throw new ApplicationException("synthetic fault")); } catch (ApplicationException) {} Check(await Gate("failure", () => Task.FromResult(4)) == 4, "lock released on exception"); Check(leases.ActiveCount == 0, "no leaked leases or caller async context"); Console.WriteLine("ALL " + passed + " CHECKS PASSED (offline; no business database or provider contacted)"); [SugarTable("test_row")] public class TestRow { public long Id { get; set; } public string Name { get; set; } } public sealed class FakeLeases { private readonly ConcurrentDictionary held = new(); private int acquisitions; public int AcquisitionCount => acquisitions; public int ActiveCount => held.Count; public Task Acquire(string key) { if (!held.TryAdd(key, 0)) throw new InvalidOperationException("busy"); Interlocked.Increment(ref acquisitions); return Task.FromResult(new Lease(() => held.TryRemove(key, out _))); } private sealed class Lease(Action release) : IAsyncDisposable { public ValueTask DisposeAsync() { release(); return ValueTask.CompletedTask; } } }