Skip to content

Commit 23a2f27

Browse files
committed
fix: simplify the NOT section of simplify_caveat and add test for the uncovered case
1 parent 08b6a05 commit 23a2f27

File tree

2 files changed

+84
-13
lines changed

2 files changed

+84
-13
lines changed

pkg/query/simplify_caveat.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,26 @@ func simplifyNotOperation(
279279
return nil, false, err
280280
}
281281

282+
// Since we're simplifying the NOT operation, the results are basically flipped.
283+
282284
if simplified == nil && passes {
283-
// Child evaluated to true unconditionally - NOT true is false
285+
// Child caveat evaluated to true unconditionally - NOT true is false
284286
return expr, false, nil // Return original expr to indicate what failed
285-
} else if !passes {
286-
// Child failed - NOT false is true
287+
}
288+
289+
if !passes {
290+
// Child caveat did NOT pass the caveat check, or simplified to impossible.
291+
// Since the NOT of false is true, we DO pass the check, unconditionally.
287292
return nil, true, nil
288-
} else {
289-
// Child is conditional - NOT conditional is still conditional
290-
return &core.CaveatExpression{
291-
OperationOrCaveat: &core.CaveatExpression_Operation{
292-
Operation: &core.CaveatOperation{
293-
Op: core.CaveatOperation_NOT,
294-
Children: []*core.CaveatExpression{simplified},
295-
},
296-
},
297-
}, true, nil
298293
}
294+
295+
// Child is conditional - NOT conditional is still conditional
296+
return &core.CaveatExpression{
297+
OperationOrCaveat: &core.CaveatExpression_Operation{
298+
Operation: &core.CaveatOperation{
299+
Op: core.CaveatOperation_NOT,
300+
Children: []*core.CaveatExpression{simplified},
301+
},
302+
},
303+
}, true, nil
299304
}

pkg/query/simplify_caveat_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,72 @@ func TestSimplifyWithEmptyContext(t *testing.T) {
12161216
require.NotNil(simplified.GetOperation())
12171217
}
12181218

1219+
func TestSimplifyNotConditional(t *testing.T) {
1220+
t.Parallel()
1221+
ctx := context.Background()
1222+
require := require.New(t)
1223+
1224+
// Create test caveat: count < limit
1225+
env, err := caveats.EnvForVariablesWithDefaultTypeSet(map[string]caveattypes.VariableType{
1226+
"count": caveattypes.Default.UIntType,
1227+
"limit": caveattypes.Default.UIntType,
1228+
})
1229+
require.NoError(err)
1230+
1231+
limitCaveat, err := caveats.CompileCaveatWithName(env, "count < limit", "limit_check")
1232+
require.NoError(err)
1233+
1234+
serialized, err := limitCaveat.Serialize()
1235+
require.NoError(err)
1236+
1237+
caveatDef := &core.CaveatDefinition{
1238+
Name: "limit_check",
1239+
SerializedExpression: serialized,
1240+
ParameterTypes: env.EncodedParametersTypes(),
1241+
}
1242+
1243+
// Create datastore with caveat
1244+
ds, err := memdb.NewMemdbDatastore(0, 0, memdb.DisableGC)
1245+
require.NoError(err)
1246+
1247+
revision, err := ds.ReadWriteTx(ctx, func(ctx context.Context, tx datastore.ReadWriteTransaction) error {
1248+
return tx.WriteCaveats(ctx, []*core.CaveatDefinition{caveatDef})
1249+
})
1250+
require.NoError(err)
1251+
1252+
reader := ds.SnapshotReader(revision)
1253+
runner := internalcaveats.NewCaveatRunner(caveattypes.Default.TypeSet)
1254+
1255+
// Create NOT expression: NOT limit_check(limit=10)
1256+
// Base case: when child is conditional (partial), NOT conditional should remain conditional
1257+
notExpr := internalcaveats.Invert(
1258+
&core.CaveatExpression{
1259+
OperationOrCaveat: &core.CaveatExpression_Caveat{
1260+
Caveat: &core.ContextualizedCaveat{
1261+
CaveatName: "limit_check",
1262+
Context: mustToStruct(t, map[string]any{"limit": uint64(10)}),
1263+
},
1264+
},
1265+
},
1266+
)
1267+
1268+
// Test with partial/missing context - child caveat is conditional, so NOT should also be conditional
1269+
// We provide limit but not count, making the child caveat partial
1270+
partialContext := map[string]any{} // No count provided, only limit is in relationship context
1271+
1272+
simplified, passes, err := SimplifyCaveatExpression(ctx, runner, notExpr, partialContext, reader)
1273+
require.NoError(err)
1274+
require.True(passes, "NOT of conditional caveat should pass conditionally")
1275+
require.NotNil(simplified, "NOT of conditional caveat should remain as NOT expression")
1276+
1277+
// Verify the structure: should still be a NOT operation with the child caveat
1278+
require.NotNil(simplified.GetOperation(), "Result should be an operation")
1279+
require.Equal(core.CaveatOperation_NOT, simplified.GetOperation().Op, "Should be a NOT operation")
1280+
require.Len(simplified.GetOperation().Children, 1, "NOT should have one child")
1281+
require.NotNil(simplified.GetOperation().Children[0].GetCaveat(), "Child should be the original caveat")
1282+
require.Equal("limit_check", simplified.GetOperation().Children[0].GetCaveat().CaveatName)
1283+
}
1284+
12191285
func TestSimplifyDeeplyNestedCaveats(t *testing.T) {
12201286
ctx := context.Background()
12211287
require := require.New(t)

0 commit comments

Comments
 (0)