๐Ÿ“ฆ sleepyfran / duets

๐Ÿ“„ Character.Events.Tests.fs ยท 268 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268module Duets.Simulation.Tests.Events.Character

open FsUnit
open NUnit.Framework
open Test.Common
open Test.Common.Generators

open Duets.Entities
open Duets.Simulation

let lowCharacterHealthEffect state =
    let character = Queries.Characters.playableCharacter state

    CharacterAttributeChanged(
        character.Id,
        CharacterAttribute.Health,
        Diff(15, 5)
    )

[<Test>]
let ``tick of low character health should generate health depleted`` () =
    let state = State.generateOne State.defaultOptions

    Simulation.tickOne state (lowCharacterHealthEffect state)
    |> fst
    |> List.item 1
    |> should be (ofCase <@ CharacterHealthDepleted @>)

[<Test>]
let ``tick of low character health should hospitalize character`` () =
    let state = State.generateOne State.defaultOptions

    Simulation.tickOne state (lowCharacterHealthEffect state)
    |> fst
    |> List.item 2
    |> should be (ofCase <@ CharacterHospitalized @>)

[<Test>]
let ``tick of low character health during concert should cancel concert`` () =
    let state =
        State.generateOne
            { State.defaultOptions with
                FutureConcertsToGenerate = 0 }

    let stateOnConcert =
        Situations.inConcert
            { Concert = dummyConcert
              Events = []
              Checklist =
                { MerchStandSetup = false
                  SoundcheckDone = false }
              Points = 0<quality> }
        |> State.Root.applyEffect state

    Simulation.tickOne stateOnConcert (lowCharacterHealthEffect state)
    |> fst
    |> List.item 1
    |> should be (ofCase <@ ConcertCancelled @>)

[<Test>]
let ``tick of low character health advances one week`` () =
    let state = State.generateOne State.defaultOptions

    let oneWeekLater = Calendar.gameBeginning |> Calendar.Ops.addDays 7<days>

    Simulation.tickOne state (lowCharacterHealthEffect state)
    |> fst
    |> should contain (TimeAdvanced oneWeekLater)

let private assertAttributeChanged attribute amount effect =
    let state = State.generateOne State.defaultOptions

    let character = Queries.Characters.playableCharacter state

    Simulation.tickOne state effect
    |> fst
    |> List.choose (fun effect ->
        match effect with
        | CharacterAttributeChanged(characterId, attr, diff) ->
            Some(characterId, attr, diff)
        | _ -> None)
    |> List.head
    |> fun (characterId, attr, Diff(_, currentAmount)) ->
        characterId |> should equal character.Id
        attr |> should equal attribute
        currentAmount |> should equal amount

[<Test>]
let ``tick of song improved should decrease energy`` () =
    SongImproved(dummyBand, Diff(dummyUnfinishedSong, dummyUnfinishedSong))
    |> assertAttributeChanged CharacterAttribute.Energy 80

[<Test>]
let ``tick of song started should decrease energy`` () =
    SongStarted(dummyBand, dummyUnfinishedSong)
    |> assertAttributeChanged CharacterAttribute.Energy 80

[<Test>]
let ``tick of song practiced should decrease energy`` () =
    SongPracticed(dummyBand, dummyFinishedSong)
    |> assertAttributeChanged CharacterAttribute.Energy 90

[<Test>]
let ``tick of passing time should decrease character's drunkenness`` () =
    let state = State.generateOne State.defaultOptions

    let character = Queries.Characters.playableCharacter state

    let stateAfterGettingDrunk =
        Character.Attribute.add character CharacterAttribute.Drunkenness 15
        |> State.Root.applyEffects state

    let character = Queries.Characters.playableCharacter stateAfterGettingDrunk

    let oneDayMomentLater =
        Calendar.gameBeginning |> Calendar.Transform.changeDayMoment Morning

    Simulation.tickOne stateAfterGettingDrunk (TimeAdvanced oneDayMomentLater)
    |> fst
    |> should
        contain
        (CharacterAttributeChanged(
            character.Id,
            CharacterAttribute.Drunkenness,
            Diff(15, 10)
        ))


[<Test>]
let ``tick of passing time should decrease character's health when passing 85 in drunkenness``
    ()
    =
    let state = State.generateOne State.defaultOptions

    let character = Queries.Characters.playableCharacter state

    let stateAfterGettingDrunk =
        Character.Attribute.add character CharacterAttribute.Drunkenness 95
        |> State.Root.applyEffects state

    let character = Queries.Characters.playableCharacter stateAfterGettingDrunk

    let oneDayMomentLater =
        Calendar.gameBeginning |> Calendar.Transform.changeDayMoment Morning

    let expectedHealth = 100 + Config.LifeSimulation.drunkHealthReduceRate

    Simulation.tickOne stateAfterGettingDrunk (TimeAdvanced oneDayMomentLater)
    |> fst
    |> should
        contain
        (CharacterAttributeChanged(
            character.Id,
            CharacterAttribute.Health,
            Diff(100, expectedHealth)
        ))

let private testConcertEffect effect moodIncrease =
    let state =
        State.generateOne
            { State.defaultOptions with
                CharacterMoodMin = 50
                CharacterMoodMax = 50 }

    let character = Queries.Characters.playableCharacter state

    Simulation.tickOne state effect
    |> fst
    |> should
        contain
        (CharacterAttributeChanged(
            character.Id,
            CharacterAttribute.Mood,
            Diff(50, 50 + moodIncrease)
        ))

let private testConcertFinishedEffect concertQuality =
    ConcertFinished(
        dummyBand,
        PerformedConcert(dummyConcert, concertQuality),
        0m<dd>
    )
    |> testConcertEffect

[<Test>]
let ``tick of ConcertCancelled should greatly decrease character's mood`` () =
    let effect =
        ConcertCancelled(
            dummyBand,
            FailedConcert(dummyConcert, BandDidNotMakeIt)
        )

    testConcertEffect effect Config.LifeSimulation.Mood.concertFailIncrease

[<Test>]
let ``tick of ConcertFinished with a bad concert should decrease character's mood``
    ()
    =
    testConcertFinishedEffect
        20<quality>
        Config.LifeSimulation.Mood.concertPoorResultIncrease

[<Test>]
let ``tick of ConcertFinished with an okay concert should increase character's mood``
    ()
    =
    testConcertFinishedEffect
        50<quality>
        Config.LifeSimulation.Mood.concertNormalResultIncrease

[<Test>]
let ``tick of ConcertFinished with a good concert should decrease character's mood``
    ()
    =
    testConcertFinishedEffect
        80<quality>
        Config.LifeSimulation.Mood.concertGoodResultIncrease

[<Test>]
let ``tick of BandFansChanged does nothing if character's fame is already at least half of the estimated fame of the band``
    ()
    =
    let state =
        dummyState
        |> State.Characters.setAttribute
            dummyCharacter.Id
            CharacterAttribute.Fame
            20

    let previousFans = [ Prague, 5000<fans> ] |> Map.ofList
    let updatedFans = [ Prague, 10000<fans> ] |> Map.ofList

    let effect = BandFansChanged(dummyBand, Diff(previousFans, updatedFans))

    Simulation.tickOne state effect
    |> fst
    |> List.filter (function
        | CharacterAttributeChanged _ -> true
        | _ -> false)
    |> should haveLength 0

[<Test>]
let ``tick of BandFansChanged updates the character's fame to be at least half of the estimated fame of the band``
    ()
    =
    let state =
        dummyState
        |> State.Characters.setAttribute
            dummyCharacter.Id
            CharacterAttribute.Fame
            8

    let previousFans = [ Prague, 5000<fans> ] |> Map.ofList

    let updatedFans = [ Prague, 10000<fans> ] |> Map.ofList

    let effect = BandFansChanged(dummyBand, Diff(previousFans, updatedFans))

    Simulation.tickOne state effect
    |> fst
    |> should
        contain
        (CharacterAttributeChanged(
            dummyCharacter.Id,
            CharacterAttribute.Fame,
            Diff(8, 16)
        ))