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); // Uses the real signing-material validity rule. End time is exclusive. var validityNow = new DateTime(2026, 9, 17, 12, 0, 0); var availableVersion = new ObContractTemplateVersion { VersionStatus = ContractTemplateVersionStatus.Enabled }; Check(ContractTemplateVersionValidity.IsAvailable(availableVersion, validityNow), "enabled version without time bounds is available"); availableVersion.EffectiveDate = validityNow; availableVersion.ExpiryDate = validityNow.AddSeconds(1); Check(ContractTemplateVersionValidity.IsAvailable(availableVersion, validityNow), "effective boundary is inclusive"); availableVersion.ExpiryDate = validityNow; Check(!ContractTemplateVersionValidity.IsAvailable(availableVersion, validityNow), "expiry boundary is exclusive"); availableVersion.EffectiveDate = validityNow.AddSeconds(1); availableVersion.ExpiryDate = validityNow.AddMinutes(1); Check(!ContractTemplateVersionValidity.IsAvailable(availableVersion, validityNow), "future version is unavailable"); availableVersion.EffectiveDate = validityNow.AddMinutes(-1); availableVersion.ExpiryDate = validityNow.AddMinutes(1); availableVersion.VersionStatus = ContractTemplateVersionStatus.Disabled; Check(!ContractTemplateVersionValidity.IsAvailable(availableVersion, validityNow), "disabled version is unavailable"); ContractTemplateVersionValidity.ValidateRange(validityNow, validityNow.AddSeconds(1)); Reject(() => ContractTemplateVersionValidity.ValidateRange(validityNow, validityNow), "equal start/end rejected"); Reject(() => ContractTemplateVersionValidity.ValidateRange(validityNow, validityNow.AddSeconds(-1)), "end before start rejected"); // Uses the real partial-update builder and ORM SQL generation, never executes the SQL. Reject(() => ContractTemplateVersionSealConfiguration.BuildUpdate(db, new() { Id = 42 }), "seal update rejects an empty patch instead of resetting both flags"); Reject(() => ContractTemplateVersionSealConfiguration.BuildUpdate(db, new() { Id = 0, CompanySealEnabled = false }), "seal update requires a specific version"); foreach (var enabled in new[] { false, true }) { foreach (var companySeal in new[] { true, false }) { var patch = new OnboardingContractVersionSealInput { Id = 42 }; var changedField = companySeal ? "CompanySealEnabled" : "AcrossPageSealEnabled"; var untouchedField = companySeal ? "AcrossPageSealEnabled" : "CompanySealEnabled"; if (companySeal) patch.CompanySealEnabled = enabled; else patch.AcrossPageSealEnabled = enabled; var generated = ContractTemplateVersionSealConfiguration.BuildUpdate(db, patch).ToSql(); var setClause = System.Text.RegularExpressions.Regex.Match(generated.Key, @"\bSET\b([\s\S]*?)\bWHERE\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Groups[1].Value; Check(setClause.Contains(changedField) && !setClause.Contains(untouchedField) && !setClause.Contains("VersionStatus") && !System.Text.RegularExpressions.Regex.IsMatch(setClause, @"\bId\b"), $"{changedField}={enabled} changes only its own column, never the other flag, status or primary key"); Check(generated.Value.Any(item => item.Value is bool flag && flag == enabled), $"{changedField}={enabled} preserves the explicit boolean value in parameterized SQL"); Check(System.Text.RegularExpressions.Regex.IsMatch(generated.Key, @"\bWHERE\b[\s\S]*\bId\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase) && generated.Value.Any(item => item.Value is long id && id == 42), $"{changedField} update is scoped to the requested version ID"); } } var bothSealUpdates = ContractTemplateVersionSealConfiguration.BuildUpdate(db, new() { Id = 42, CompanySealEnabled = false, AcrossPageSealEnabled = true }).ToSql().Key; Check(bothSealUpdates.Contains("CompanySealEnabled") && bothSealUpdates.Contains("AcrossPageSealEnabled"), "both seal flags can be explicitly updated without overwriting unrelated version data"); // 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"); // Uses the real request builder and provider models. No provider or database requests. var sealSettings = new QiyuesuoSettingOptions { EnableAcrossPageSeal = true, AcrossPageSealType = "SINGLE_DOCUMENT" }; var signDocuments = new[] { new OnboardingSigningDocument("labor", "员工签字", "公司盖章", true, true), new OnboardingSigningDocument("confidentiality", "员工签字", "公司盖章", true, true), new OnboardingSigningDocument("notice", "员工签字", null, false, false), new OnboardingSigningDocument("no_release_certificate", "员工签字", null, false, false) }; var positions = QiyuesuoContractRequestBuilder.BuildStampers(signDocuments, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "PERSONAL") == 4, "all four materials require employee signatures"); Check(positions.Where(item => item.Type == "PERSONAL").Select(item => item.DocumentId).SequenceEqual(signDocuments.Select(item => item.DocumentId)), "employee signatures retain each distinct material and its order"); Check(positions.Count(item => item.Type == "COMPANY") == 2, "only labor contract and confidentiality agreement receive company seals"); Check(positions.Count(item => item.Type == "ACROSS_PAGE") == 2, "selected materials receive separate across-page seals"); Check(positions.Where(item => item.Type != "PERSONAL").All(item => item.DocumentId is "labor" or "confidentiality"), "employee-only materials never receive any company signing position"); Check(positions.Where(item => item.Type == "COMPANY").All(item => item.ActionId == "company" && item.SealId == "123"), "company positions use the intended action and company seal"); sealSettings.AcrossPageSealType = "ALL_DOCUMENT"; var reorderedDocuments = new[] { signDocuments[2], signDocuments[0], signDocuments[3], signDocuments[1] }; positions = QiyuesuoContractRequestBuilder.BuildStampers(reorderedDocuments, "person", "company", "123", sealSettings); var mergedAcross = positions.Single(item => item.Type == "ACROSS_PAGE"); Check(mergedAcross.DocumentId == "labor", "merged across-page anchor is the first selected material, not the first uploaded file"); Check(mergedAcross.DocumentIds == "labor,confidentiality", "merged across-page seal excludes both employee-only materials"); sealSettings.EnableAcrossPageSeal = false; var legacyDocuments = new[] { new OnboardingSigningDocument("legacy", "员工签字", "公司盖章", null, null) }; positions = QiyuesuoContractRequestBuilder.BuildStampers(legacyDocuments, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "COMPANY") == 1 && positions.All(item => item.Type != "ACROSS_PAGE"), "old versions retain company seals and the disabled global across-page setting"); sealSettings.EnableAcrossPageSeal = true; positions = QiyuesuoContractRequestBuilder.BuildStampers(legacyDocuments, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "ACROSS_PAGE") == 1, "old versions retain the enabled global across-page setting"); sealSettings.EnableAcrossPageSeal = false; positions = QiyuesuoContractRequestBuilder.BuildStampers(signDocuments, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "ACROSS_PAGE") == 1, "new explicit across-page switch overrides legacy global fallback"); var acrossOnly = new[] { new OnboardingSigningDocument("across_only", "员工签字", null, false, true) }; positions = QiyuesuoContractRequestBuilder.BuildStampers(acrossOnly, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "COMPANY") == 0 && positions.Count(item => item.Type == "ACROSS_PAGE") == 1, "company seal and across-page seal switches operate independently"); Check(QiyuesuoContractRequestBuilder.RequiresCompanySign(false, true, sealSettings), "across-page-only materials still require a company signing action"); var companyOnly = new[] { new OnboardingSigningDocument("company_only", "员工签字", "公司盖章", true, false) }; positions = QiyuesuoContractRequestBuilder.BuildStampers(companyOnly, "person", "company", "123", sealSettings); Check(positions.Count(item => item.Type == "COMPANY") == 1 && positions.All(item => item.Type != "ACROSS_PAGE"), "company seal without across-page seal is supported"); var personalOnly = new[] { new OnboardingSigningDocument("personal_only", "员工签字", null, false, false) }; positions = QiyuesuoContractRequestBuilder.BuildStampers(personalOnly, "person", null, null, sealSettings); Check(positions.Count == 1 && positions[0].Type == "PERSONAL", "all switches off produces only personal signature positions"); Check(!QiyuesuoContractRequestBuilder.RequiresCompanySign(false, false, sealSettings), "employee-only signing needs no company action"); Reject(() => QiyuesuoContractRequestBuilder.BuildStampers(signDocuments, "person", null, "123", sealSettings), "company action cannot be silently omitted for materials requiring seals"); Reject(() => QiyuesuoContractRequestBuilder.BuildStampers( new[] { new OnboardingSigningDocument(null, "员工签字", "公司盖章", true, true) }, "person", "company", "123", sealSettings), "missing document ID is rejected instead of broadening seal scope"); sealSettings.AcrossPageSealType = "INVALID"; Reject(() => QiyuesuoContractRequestBuilder.BuildStampers(acrossOnly, "person", "company", "123", sealSettings), "invalid across-page mode is checked even when the global fallback is off"); var draftInput = new QiyuesuoCreateDraftInput { RequiresCompanySign = false, CompanyName = "测试企业", EmployeeName = "测试员工", EmployeeMobile = "13800138000", CreatorName = "测试经办人", CreatorMobile = "13900139000", CategoryId = "456", CallbackUrl = " https://example.invalid/callback " }; var employeeOnlyDraft = System.Text.Json.JsonSerializer.SerializeToElement(QiyuesuoContractRequestBuilder.BuildDraft(draftInput)); Check(employeeOnlyDraft.GetProperty("signatories").GetArrayLength() == 1 && employeeOnlyDraft.GetProperty("signatories")[0].GetProperty("tenantType").GetString() == "PERSONAL", "employee-only draft never creates a company signature node"); Check(employeeOnlyDraft.GetProperty("tenantName").GetString() == "测试企业", "employee-only workflow retains its sponsoring company"); Check(employeeOnlyDraft.GetProperty("callbackUrl").GetString() == "https://example.invalid/callback", "draft builder preserves normalized server callback"); draftInput.RequiresCompanySign = true; Reject(() => QiyuesuoContractRequestBuilder.BuildDraft(draftInput), "company signing draft requires a valid seal"); draftInput.SealId = "123"; var companyDraft = System.Text.Json.JsonSerializer.SerializeToElement(QiyuesuoContractRequestBuilder.BuildDraft(draftInput)); Check(companyDraft.GetProperty("signatories").GetArrayLength() == 2 && companyDraft.GetProperty("signatories")[1].GetProperty("actions")[0].GetProperty("type").GetString() == "COMPANY", "mixed materials keep one company signing action after employee signing"); Check(!companyDraft.GetProperty("signatories")[1].GetProperty("actions")[0].GetProperty("autoSign").GetBoolean(), "company seal remains manually triggered"); var employeeOnlyProvider = new QiyuesuoContractResult { Signatories = new() { new QiyuesuoSignatoryResult { TenantType = "PERSONAL", Status = "SIGNED" } }}; Check(QiyuesuoContractRequestBuilder.ResolvePendingStatus(employeeOnlyProvider) != ElectronicSignStatus.PendingCompanySeal, "employee-only workflow cannot become stuck in a nonexistent company-seal stage"); employeeOnlyProvider.Signatories.Add(new QiyuesuoSignatoryResult { TenantType = "COMPANY", Actions = new() { new QiyuesuoActionResult { Type = "COMPANY", Status = "START" } }}); Check(QiyuesuoContractRequestBuilder.ResolvePendingStatus(employeeOnlyProvider) == ElectronicSignStatus.PendingCompanySeal, "mixed workflow waits for company seal after employee signing"); 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; } } }