using Godosa.Core.Runs.Scripts; using Xunit; namespace Godosa.Core.Tests.Runs.Scripts; public class ScriptValidatorTests { private static FakeGame Game() { var g = new FakeGame(); g.ProbeFns["quest"] = _ => RunValue.Of(0); g.ProbeFns["dialog-open"] = _ => RunValue.Of(false); g.ActionTable["talk"] = new FakeAction(VerbKind.Player, _ => new ActionStart(), min: 1, max: 1); g.ActionTable["cheat"] = new FakeAction(VerbKind.Debug, _ => new ActionStart(Status: StepStatus.Done), min: 1, max: 20); return g; } private static string Validate(string main, bool deterministic = true, FakeGame? game = null) => string.Join(" | ", ScriptValidator.Validate( ScriptLoader.Load("main.steps", new MemoryScriptSource(new Dictionary { ["main.steps"] = main }), new Dictionary()), game ?? Game(), deterministic).Select(e => e.ToString())); [Fact] public void ValidPlayerScript_NoErrors() => Assert.Equal("", Validate("format 1\nmode player\nsetup:\n cheat gold 5\ntalk @obj(Virgil)\nwait dialog-open 2s\nexpect quest(1) >= accepted\nclick 1,2\nkey R\nlog \"hi\"")); [Theory] [InlineData("format 1\nmode player\ndance", "main.steps:3: unknown verb dance")] [InlineData("format 1\nmode player\nwalk-to 3,4", "main.steps:3: verb walk-to not supported by game fake")] [InlineData("format 1\nmode player\nclick", "main.steps:3: verb click takes 1..1 arguments, got 0")] [InlineData("format 1\nmode player\ncheat gold 5", "main.steps:3: debug verb cheat outside setup in player mode")] [InlineData("format 1\nmode player\nwait nope 10", "main.steps:3: unknown probe or constant nope")] [InlineData("format 1\nmode player\nexpect quest(1) >=", "main.steps:3: condition ends early")] [InlineData("format 1\nmode player\ntalk @npc(Virgil)", "main.steps:3: unknown locator @npc")] [InlineData("format 1\nmode player\ntry:\n key R", "main.steps:3: try needs a non-deterministic run")] [InlineData("format 1\nmode player\noutput loud", "main.steps:3: output level must be quiet|checkpoints|labels|steps|trace")] public void Errors(string main, string error) => Assert.Contains(error, Validate(main)); [Fact] public void DebugMode_DebugVerbAnywhere() => Assert.Equal("", Validate("format 1\nmode debug\ncheat gold 5\nkey R")); [Fact] public void Try_AllowedNonDeterministic() => Assert.Equal("", Validate("format 1\nmode player\ntry:\n key R", deterministic: false)); [Fact] public void Rng_NeedsTheGameHook() { var g = Game(); g.RngImpl = null; Assert.Contains("main.steps:3: verb rng not supported by game fake", Validate("format 1\nmode player\nrng seed 4", game: g)); } [Fact] public void GameVerb_ShadowingALibVerbWithAnotherKind_Rejected() { var g = Game(); g.ActionTable["click"] = new FakeAction(VerbKind.Debug, _ => new ActionStart()); Assert.Contains("game verb click shadows a lib verb", Validate("format 1\nmode debug\nkey R", game: g)); } [Fact] public void LoaderErrorsComeThrough() => Assert.Contains("main.steps:3: unknown def x", Validate("format 1\nmode debug\ncall x()")); }