Skip to content

Commit 504efaf

Browse files
committed
Relocate condition validation tests onto the conditions
Now that each condition validates itself, move the per-kind validation assertions to the condition tests: IpAllowlistConditionTests (empty/invalid CIDR), TimeOfDayConditionTests (missing/unknown tz, no windows), and TimeWindowTests (no days, malformed HH:mm bounds). Trim AccessRuleValidatorTests to the validator's remaining job — JSON parse/shape/size, null entry, and delegation — keeping the unknown-weekday-token case as a deserialization concern. Mirrors the earlier relocation of the evaluation tests onto the conditions.
1 parent 253c6ec commit 504efaf

3 files changed

Lines changed: 109 additions & 58 deletions

File tree

bitwarden_license/test/Services/Pam.Test/Models/Conditions/IpAllowlistConditionTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,33 @@ public void Evaluate_LaterCidrMatches_Allows()
7777

7878
Assert.Equal(AccessEvaluationOutcome.Allow, evaluation.Outcome);
7979
}
80+
81+
[Fact]
82+
public void Validate_NoCidrs_IsInvalid()
83+
{
84+
var result = new IpAllowlistCondition().Validate();
85+
86+
Assert.False(result.IsValid);
87+
Assert.Contains("at least one CIDR", result.Error);
88+
}
89+
90+
[Theory]
91+
[InlineData("")]
92+
[InlineData("not-a-cidr")]
93+
[InlineData("10.0.0.0/99")]
94+
public void Validate_InvalidCidr_IsInvalid(string cidr)
95+
{
96+
var result = new IpAllowlistCondition { Cidrs = [cidr] }.Validate();
97+
98+
Assert.False(result.IsValid);
99+
Assert.Contains("Invalid CIDR", result.Error);
100+
}
101+
102+
[Fact]
103+
public void Validate_ValidCidrs_IsValid()
104+
{
105+
var result = new IpAllowlistCondition { Cidrs = ["10.0.0.0/8", "2001:db8::/32"] }.Validate();
106+
107+
Assert.True(result.IsValid);
108+
}
80109
}

bitwarden_license/test/Services/Pam.Test/Models/Conditions/TimeOfDayConditionTests.cs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,46 @@ public void Evaluate_LaterWindowMatches_Allows()
120120

121121
Assert.Equal(AccessEvaluationOutcome.Allow, condition.Evaluate(Signals()).Outcome);
122122
}
123+
124+
[Fact]
125+
public void Validate_MissingTz_IsInvalid()
126+
{
127+
var result = new TimeOfDayCondition { Windows = [ValidWindow()] }.Validate();
128+
129+
Assert.False(result.IsValid);
130+
Assert.Contains("tz", result.Error);
131+
}
132+
133+
[Fact]
134+
public void Validate_UnknownTz_IsInvalid()
135+
{
136+
var result = new TimeOfDayCondition { Tz = "Invalid/Zone", Windows = [ValidWindow()] }.Validate();
137+
138+
Assert.False(result.IsValid);
139+
Assert.Contains("timezone", result.Error);
140+
}
141+
142+
[Fact]
143+
public void Validate_NoWindows_IsInvalid()
144+
{
145+
var result = new TimeOfDayCondition { Tz = "UTC", Windows = [] }.Validate();
146+
147+
Assert.False(result.IsValid);
148+
Assert.Contains("at least one window", result.Error);
149+
}
150+
151+
[Fact]
152+
public void Validate_Valid_IsValid()
153+
{
154+
var result = new TimeOfDayCondition { Tz = "UTC", Windows = [ValidWindow()] }.Validate();
155+
156+
Assert.True(result.IsValid);
157+
}
158+
159+
private static TimeWindow ValidWindow() => new() { Days = [AccessWeekday.Mon], From = "09:00", To = "17:00" };
123160
}
124161

125-
public class TimeWindowContainsTests
162+
public class TimeWindowTests
126163
{
127164
private static TimeWindow BusinessHours() => new()
128165
{
@@ -177,4 +214,41 @@ public void Contains_UnparseableBounds_FailsClosed()
177214

178215
Assert.False(window.Contains(DayOfWeek.Thursday, new TimeOnly(12, 0)));
179216
}
217+
218+
[Fact]
219+
public void Validate_NoDays_IsInvalid()
220+
{
221+
var result = new TimeWindow { Days = [], From = "09:00", To = "17:00" }.Validate();
222+
223+
Assert.False(result.IsValid);
224+
Assert.Contains("at least one day", result.Error);
225+
}
226+
227+
[Theory]
228+
[InlineData("9am", "17:00")]
229+
[InlineData("25:00", "17:00")]
230+
public void Validate_InvalidFrom_IsInvalid(string from, string to)
231+
{
232+
var result = new TimeWindow { Days = [AccessWeekday.Mon], From = from, To = to }.Validate();
233+
234+
Assert.False(result.IsValid);
235+
Assert.Contains("Expected HH:mm", result.Error);
236+
}
237+
238+
[Fact]
239+
public void Validate_InvalidTo_IsInvalid()
240+
{
241+
var result = new TimeWindow { Days = [AccessWeekday.Mon], From = "09:00", To = "5pm" }.Validate();
242+
243+
Assert.False(result.IsValid);
244+
Assert.Contains("Expected HH:mm", result.Error);
245+
}
246+
247+
[Fact]
248+
public void Validate_ValidWindow_IsValid()
249+
{
250+
var result = new TimeWindow { Days = [AccessWeekday.Mon], From = "09:00", To = "17:00" }.Validate();
251+
252+
Assert.True(result.IsValid);
253+
}
180254
}

bitwarden_license/test/Services/Pam.Test/Services/AccessRuleValidatorTests.cs

Lines changed: 5 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -62,66 +62,14 @@ public void Validate_LegacyAllOfKind_IsInvalid()
6262
}
6363

6464
[Fact]
65-
public void Validate_HumanApproval_IsValid()
65+
public void Validate_TimeOfDay_UnknownWeekdayToken_IsInvalid()
6666
{
67-
var result = _sut.Validate("""[{"kind":"human_approval"}]""");
68-
69-
Assert.True(result.IsValid);
70-
}
71-
72-
[Theory]
73-
[InlineData("""[{"kind":"ip_allowlist","cidrs":["10.0.0.0/8"]}]""")]
74-
[InlineData("""[{"kind":"ip_allowlist","cidrs":["10.0.0.0/8","192.168.0.0/16","2001:db8::/32"]}]""")]
75-
public void Validate_IpAllowlist_ValidCidrs_IsValid(string conditionsJson)
76-
{
77-
var result = _sut.Validate(conditionsJson);
78-
79-
Assert.True(result.IsValid);
80-
}
81-
82-
[Theory]
83-
[InlineData("""[{"kind":"ip_allowlist","cidrs":[]}]""", "at least one CIDR")]
84-
[InlineData("""[{"kind":"ip_allowlist","cidrs":["not-a-cidr"]}]""", "Invalid CIDR")]
85-
[InlineData("""[{"kind":"ip_allowlist","cidrs":["10.0.0.0/99"]}]""", "Invalid CIDR")]
86-
public void Validate_IpAllowlist_InvalidCidrs_IsInvalid(string conditionsJson, string expectedMessageFragment)
87-
{
88-
var result = _sut.Validate(conditionsJson);
89-
90-
Assert.False(result.IsValid);
91-
Assert.Contains(expectedMessageFragment, result.Error);
92-
}
93-
94-
[Fact]
95-
public void Validate_TimeOfDay_Valid_IsValid()
96-
{
97-
var result = _sut.Validate("""
98-
[
99-
{
100-
"kind": "time_of_day",
101-
"tz": "UTC",
102-
"windows": [
103-
{ "days": ["mon","tue","wed","thu","fri"], "from": "09:00", "to": "18:00" }
104-
]
105-
}
106-
]
107-
""");
108-
109-
Assert.True(result.IsValid);
110-
}
111-
112-
[Theory]
113-
[InlineData("""[{"kind":"time_of_day","tz":"Invalid/Zone","windows":[{"days":["mon"],"from":"09:00","to":"17:00"}]}]""", "timezone")]
114-
[InlineData("""[{"kind":"time_of_day","tz":"UTC","windows":[]}]""", "at least one window")]
115-
[InlineData("""[{"kind":"time_of_day","tz":"UTC","windows":[{"days":[],"from":"09:00","to":"17:00"}]}]""", "at least one day")]
116-
[InlineData("""[{"kind":"time_of_day","tz":"UTC","windows":[{"days":["funday"],"from":"09:00","to":"17:00"}]}]""", "day")]
117-
[InlineData("""[{"kind":"time_of_day","tz":"UTC","windows":[{"days":["mon"],"from":"9am","to":"5pm"}]}]""", "Expected HH:mm")]
118-
[InlineData("""[{"kind":"time_of_day","tz":"UTC","windows":[{"days":["mon"],"from":"25:00","to":"26:00"}]}]""", "Expected HH:mm")]
119-
public void Validate_TimeOfDay_Invalid_IsInvalid(string conditionsJson, string expectedMessageFragment)
120-
{
121-
var result = _sut.Validate(conditionsJson);
67+
// An unknown weekday token fails deserialization (via AccessWeekdayJsonConverter) before any condition-level
68+
// check runs, so the whole document is rejected as malformed. Per-condition rules are covered by the
69+
// condition tests (IpAllowlistConditionTests, TimeOfDayConditionTests).
70+
var result = _sut.Validate("""[{"kind":"time_of_day","tz":"UTC","windows":[{"days":["funday"],"from":"09:00","to":"17:00"}]}]""");
12271

12372
Assert.False(result.IsValid);
124-
Assert.Contains(expectedMessageFragment, result.Error);
12573
}
12674

12775
[Fact]

0 commit comments

Comments
 (0)