Skip to content

Commit

Permalink
Add tests (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
Smaug123 authored Aug 6, 2023
1 parent 6f1fbeb commit 3a975d7
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 27 deletions.
10 changes: 10 additions & 0 deletions Gitea.Declarative.Lib/Exception.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Gitea.Declarative

open System.Runtime.ExceptionServices

[<RequireQualifiedAccess>]
module internal Exception =
let reraiseWithOriginalStackTrace<'a> (e : exn) : 'a =
let edi = ExceptionDispatchInfo.Capture e
edi.Throw ()
failwith "unreachable"
1 change: 1 addition & 0 deletions Gitea.Declarative.Lib/Gitea.Declarative.Lib.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Map.fs" />
<Compile Include="Exception.fs" />
<Compile Include="Async.fs" />
<Compile Include="GiteaClient.fs" />
<Compile Include="IGiteaClient.fs" />
Expand Down
3 changes: 3 additions & 0 deletions Gitea.Declarative.Test/Gitea.Declarative.Test.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

<ItemGroup>
<Compile Include="Utils.fs" />
<Compile Include="Result.fs" />
<Compile Include="Logging.fs" />
<Compile Include="TestUser.fs" />
<Compile Include="TestRepo.fs" />
<Compile Include="TestJsonSchema.fs" />
<Compile Include="TestSwaggerJson.fs" />
<Content Include="GiteaConfig.json" />
Expand Down
34 changes: 34 additions & 0 deletions Gitea.Declarative.Test/Logging.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Gitea.Declarative.Test

open System
open Microsoft.Extensions.Logging

[<RequireQualifiedAccess>]
module LoggerFactory =

/// Creates a test ILoggerFactory, a sink whose provided inputs you can access through the `unit -> string list`.
let makeTest () : ILoggerFactory * (unit -> string list) =
let outputs = ResizeArray<_> ()

let lf =
{ new ILoggerFactory with
member _.Dispose () = ()

member _.CreateLogger (name : string) =
{ new ILogger with
member _.IsEnabled _ = true

member _.BeginScope _ =
{ new IDisposable with
member _.Dispose () = ()
}

member _.Log (_, _, state, exc : exn, formatter) =
let toWrite = formatter.Invoke (state, exc)
lock outputs (fun () -> outputs.Add toWrite)
}

member _.AddProvider provider = failwith "unsupported"
}

lf, (fun () -> lock outputs (fun () -> Seq.toList outputs))
14 changes: 14 additions & 0 deletions Gitea.Declarative.Test/Result.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Gitea.Declarative.Test

[<RequireQualifiedAccess>]
module Result =

let get r =
match r with
| Ok o -> o
| Error e -> failwithf "Expected Ok, got: %+A" e

let getError r =
match r with
| Ok o -> failwithf "Expected Error, got: %+A" o
| Error e -> e
234 changes: 234 additions & 0 deletions Gitea.Declarative.Test/TestRepo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
namespace Gitea.Declarative.Test

open System
open System.Threading.Tasks
open Gitea.Declarative
open Gitea.InMemory
open Microsoft.Extensions.Logging.Abstractions
open NUnit.Framework
open FsUnitTyped
open FsCheck

[<TestFixture>]
module TestRepo =

[<Test>]
let ``We refuse to delete a repo if we get to Reconcile without positive confirmation`` () =
let property (gitHubToken : string option) =
let client = GiteaClientMock.Unimplemented

let lf, messages = LoggerFactory.makeTest ()
let logger = lf.CreateLogger "test"

[
User "username", Map.ofList [ RepoName "repo", AlignmentError.UnexpectedlyPresent ]
]
|> Map.ofList
|> Gitea.reconcileRepoErrors logger client gitHubToken
|> Async.RunSynchronously

messages ()
|> List.filter (fun s -> s.Contains ("refusing to delete", StringComparison.OrdinalIgnoreCase))
|> List.length
|> shouldEqual 1

Check.QuickThrowOnFailure property

[<Test>]
let ``We refuse to delete repos when they're not configured to be deleted`` () =
Arb.register<CustomArb> () |> ignore

let property
(user : User)
(repos : Map<RepoName, Repo>)
(userInfo : UserInfo)
(repo : Gitea.Repository)
(reposToReturn : Gitea.Repository[])
=
let reposToReturn = Array.append [| repo |] reposToReturn

let reposToReturn =
if reposToReturn.Length >= 5 then
reposToReturn.[0..3]
else
reposToReturn

let lf, messages = LoggerFactory.makeTest ()
let logger = lf.CreateLogger "test"

let client =
{ GiteaClientMock.Unimplemented with
UserListRepos =
fun (_username, _page, _limit) ->
async {
return
reposToReturn
|> Array.filter (fun r -> not (repos.ContainsKey (RepoName r.Name)))
}
|> Async.StartAsTask

RepoListPushMirrors = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListBranchProtection = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListCollaborators = fun _ -> async { return [||] } |> Async.StartAsTask
}

let config : GiteaConfig =
{
Users = Map.ofList [ user, userInfo ]
Repos =
let repos =
repos
|> Map.map (fun _ r ->
{ r with
Deleted =
match r.Deleted with
| Some true -> Some false
| _ -> None
}
)

[ user, repos ] |> Map.ofList
}

let recoveredUser, error =
Gitea.checkRepos logger config client
|> Async.RunSynchronously
|> Result.getError
|> Map.toSeq
|> Seq.exactlyOne

recoveredUser |> shouldEqual user

for repoName, _configuredRepo in Map.toSeq repos do
match Map.tryFind repoName error with
| Some (AlignmentError.DoesNotExist _) -> ()
| a -> failwithf "Failed: %+A" a

let messages = messages ()
messages |> shouldEqual []

Check.QuickThrowOnFailure property

[<Test>]
let ``We point out when repos have been deleted`` () =
Arb.register<CustomArb> () |> ignore

let property (user : User) (repos : Map<RepoName, Repo>) (userInfo : UserInfo) =

let lf, messages = LoggerFactory.makeTest ()
let logger = lf.CreateLogger "test"

let client =
{ GiteaClientMock.Unimplemented with
UserListRepos = fun _ -> Task.FromResult [||]

RepoListPushMirrors = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListBranchProtection = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListCollaborators = fun _ -> async { return [||] } |> Async.StartAsTask
}

let config : GiteaConfig =
{
Users = Map.ofList [ user, userInfo ]
Repos =
let repos =
repos
|> Map.map (fun _ r ->
{ r with
Deleted = Some true
}
)

[ user, repos ] |> Map.ofList
}

Gitea.checkRepos logger config client |> Async.RunSynchronously |> Result.get

for message in messages () do
message.Contains ("Remove this repo from configuration", StringComparison.OrdinalIgnoreCase)
|> shouldEqual true

Check.QuickThrowOnFailure property

[<Test>]
let ``We decide to delete repos which are configured to Deleted = true`` () =
Arb.register<CustomArb> () |> ignore

let property
(user : User)
(oneExistingRepoName : RepoName)
(oneExistingRepo : Repo)
(existingRepos : Map<RepoName, Repo>)
(userInfo : UserInfo)
=

let existingRepos = existingRepos |> Map.add oneExistingRepoName oneExistingRepo

let giteaUser =
let result = Gitea.User ()
result.LoginName <- user.ToString ()
result

let client =
{ GiteaClientMock.Unimplemented with
UserListRepos =
fun _ ->
async {
return
existingRepos
|> Map.toSeq
|> Seq.map (fun (RepoName repoName, _repoSpec) ->
let repo = Gitea.Repository ()
repo.Name <- repoName
repo.Owner <- giteaUser
repo
)
|> Seq.toArray
}
|> Async.StartAsTask

RepoListPushMirrors = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListBranchProtection = fun _ -> async { return [||] } |> Async.StartAsTask

RepoListCollaborators = fun _ -> async { return [||] } |> Async.StartAsTask
}

let config : GiteaConfig =
{
Users = Map.ofList [ user, userInfo ]
Repos =
let repos =
existingRepos
|> Map.map (fun _ r ->
{ r with
Deleted = Some true
}
)

[ user, repos ] |> Map.ofList
}

let recoveredUser, errors =
Gitea.checkRepos NullLogger.Instance config client
|> Async.RunSynchronously
|> Result.getError
|> Map.toSeq
|> Seq.exactlyOne

recoveredUser |> shouldEqual user

CollectionAssert.AreEqual (existingRepos.Keys, errors.Keys)

for _repo, config in Map.toSeq errors do
match config with
| AlignmentError.ConfigurationDiffers (desired, _) -> desired.Deleted |> shouldEqual (Some true)
| a -> failwithf "Unexpected alignment: %+A" a

Check.QuickThrowOnFailure property

// TODO: test that we delete repos which come up as ConfigurationDiffers (desired.Deleted = Some true)
Loading

0 comments on commit 3a975d7

Please sign in to comment.