quorate/scripts/hooks/hook-tests.fsx

165 lines
6.3 KiB
Text
Raw Permalink Normal View History

open System
open System.Diagnostics
open System.IO
type Result = { Code: int; Output: string; Error: string }
type Fixture = { Root: string; Source: string; Mask: string }
let require condition message =
if not condition then invalidOp message
let run root environment executable arguments =
let start = ProcessStartInfo executable
start.WorkingDirectory <- root
start.UseShellExecute <- false
start.RedirectStandardOutput <- true
start.RedirectStandardError <- true
for argument in arguments do start.ArgumentList.Add argument
for name, value in environment do start.Environment[name] <- value
use child = Process.Start start
let output = child.StandardOutput.ReadToEndAsync()
let error = child.StandardError.ReadToEndAsync()
child.WaitForExit()
{ Code = child.ExitCode; Output = output.Result; Error = error.Result }
let git fixture arguments =
let result = run fixture.Root [] "git" arguments
require (result.Code = 0) result.Error
result.Output
let write path (text: string) = File.WriteAllText(path, text)
let stage fixture = git fixture [ "add"; "." ] |> ignore
let hook = Path.Combine(__SOURCE_DIRECTORY__, "pre-commit")
let runHook fixture environment =
let before = git fixture [ "ls-files"; "--stage"; "-z" ]
let result = run fixture.Root environment hook []
require (before = git fixture [ "ls-files"; "--stage"; "-z" ]) "hook changed the index"
result
let passes fixture =
let result = runHook fixture []
require (result.Code = 0) result.Error
let fails expected fixture =
let result = runHook fixture []
require (result.Code <> 0) "hook unexpectedly succeeded"
require (result.Error.Contains(expected: string)) result.Error
let directory fixture path = Directory.CreateDirectory(Path.Combine(fixture.Root, path)).FullName
let tests: (string * (Fixture -> unit)) list =
[ "staged tree", fun fixture ->
write fixture.Source "unstaged source edit\n"
write fixture.Mask "unstaged invalid mask\n"
passes fixture
"unrelated change", fun fixture ->
write fixture.Source "other=changed\nvalue=synthetic\n"
stage fixture
passes fixture
"missing match", fun fixture ->
write fixture.Source "value=changed\n"
stage fixture
fails "no match" fixture
"multiple matches", fun fixture ->
write fixture.Source "synthetic synthetic\n"
stage fixture
fails "multiple matches" fixture
"overlapping matches", fun fixture ->
write fixture.Source "aaa"
write fixture.Mask "[[replace]]\nbefore = 'aa'\nafter = 'b'\n"
stage fixture
fails "multiple matches" fixture
"empty before", fun fixture ->
write fixture.Mask "[[replace]]\nbefore = ''\nafter = 'x'\n"
stage fixture
fails "before is empty" fixture
"ordered rules", fun fixture ->
write fixture.Mask
("[[replace]]\nbefore = 'synthetic'\nafter = 'intermediate'\n"
+ "[[replace]]\nbefore = 'intermediate'\nafter = '********'\n")
stage fixture
passes fixture
"invalid TOML", fun fixture ->
write fixture.Mask "invalid = ["
stage fixture
let result = runHook fixture []
require (result.Code <> 0) "invalid TOML was accepted"
"invalid schema", fun fixture ->
write fixture.Mask "[[replace]]\nbefore = 'synthetic'\nafter = 1\n"
stage fixture
fails "expected text" fixture
"missing target", fun fixture ->
git fixture [ "rm"; "--cached"; "config.txt" ] |> ignore
fails "target is not a regular file" fixture
"symlink target", fun fixture ->
File.Delete fixture.Source
File.CreateSymbolicLink(fixture.Source, "elsewhere") |> ignore
stage fixture
fails "target is not a regular file" fixture
"omitted subtree", fun fixture ->
let hidden = directory fixture "hidden"
write (Path.Combine(hidden, ".omit")) ""
write (Path.Combine(hidden, "invalid.mask.toml")) "invalid"
let nested = directory fixture "hidden/nested"
write (Path.Combine(nested, ".omit")) ""
write (Path.Combine(nested, "legacy.mask.diff")) "invalid"
stage fixture
let result = runHook fixture []
require (result.Code = 0) result.Error
require (result.Output.Contains "1 directories omitted") result.Output
"omit prefix boundary", fun fixture ->
let hidden = directory fixture "hidden"
write (Path.Combine(hidden, ".omit")) ""
let peer = directory fixture "hidden-peer"
write (Path.Combine(peer, "invalid.mask.toml")) "invalid"
stage fixture
fails "target is not a regular file" fixture
"unstaged omit", fun fixture ->
write fixture.Source "synthetic synthetic"
stage fixture
write (Path.Combine(fixture.Root, ".omit")) ""
fails "multiple matches" fixture
"legacy format", fun fixture ->
write (Path.Combine(fixture.Root, "old.mask.diff")) "invalid"
stage fixture
fails "legacy mask format" fixture
"alternate index", fun fixture ->
let index = Path.Combine(fixture.Root, "alternate")
File.Copy(Path.Combine(fixture.Root, ".git/index"), index)
write fixture.Source "staged drift\n"
git fixture [ "add"; "config.txt" ] |> ignore
let before = File.ReadAllBytes index
let result = runHook fixture [ "GIT_INDEX_FILE", index ]
require (result.Code = 0) result.Error
require (before = File.ReadAllBytes index) "hook changed the alternate index" ]
for name, test in tests do
let root = Directory.CreateDirectory(Path.Combine(fsi.CommandLineArgs[1], $"hook-tests-{Guid.NewGuid():N}")).FullName
let fixture = { Root = root; Source = Path.Combine(root, "config.txt"); Mask = Path.Combine(root, "config.txt.mask.toml") }
try
git fixture [ "init"; "-q" ] |> ignore
write fixture.Source "value=synthetic\n"
write fixture.Mask "[[replace]]\nbefore = 'synthetic'\nafter = '********'\n"
stage fixture
test fixture
printfn "passed: %s" name
finally
Directory.Delete(root, true)
printfn "%d hook tests passed" tests.Length