Skip to content

Commit fb8519e

Browse files
l46kokcopybara-github
authored andcommitted
Add CelLiteRuntimeVersionSkewTest for runtime version skew verification
PiperOrigin-RevId: 984133626
1 parent 798c0be commit fb8519e

16 files changed

Lines changed: 2852 additions & 14 deletions

File tree

‎checker/src/main/java/dev/cel/checker/CelStandardDeclarations.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,8 @@ public enum StandardIdentifier {
15531553
DOUBLE(newStandardIdentDecl(SimpleType.DOUBLE)),
15541554
BYTES(newStandardIdentDecl(SimpleType.BYTES)),
15551555
STRING(newStandardIdentDecl(SimpleType.STRING)),
1556+
DURATION(newStandardIdentDecl(SimpleType.DURATION)),
1557+
TIMESTAMP(newStandardIdentDecl(SimpleType.TIMESTAMP)),
15561558
DYN(newStandardIdentDecl(SimpleType.DYN)),
15571559
TYPE(newStandardIdentDecl("type", SimpleType.DYN)),
15581560
NULL_TYPE(newStandardIdentDecl("null_type", SimpleType.NULL_TYPE)),

‎checker/src/main/java/dev/cel/checker/ExprChecker.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,8 @@ private void visit(CelMutableExpr expr, CelMutableStruct struct) {
379379
env.reportError(expr.id(), getPosition(expr), "'%s' is not a type", CelTypes.format(type));
380380
} else {
381381
messageType = ((TypeType) type).type();
382-
if (!messageType.kind().equals(CelKind.STRUCT)) {
382+
if (!messageType.kind().equals(CelKind.STRUCT)
383+
&& !CelTypes.isWellKnownType(messageType.name())) {
383384
env.reportError(
384385
expr.id(),
385386
getPosition(expr),
@@ -816,7 +817,7 @@ private CelType getFieldType(long exprId, int position, CelType type, String fie
816817
// provided
817818
String errorMessage =
818819
String.format("Message type resolution failure while referencing field '%s'.", fieldName);
819-
if (type.kind().equals(CelKind.STRUCT)) {
820+
if (type.kind().equals(CelKind.STRUCT) || CelTypes.isWellKnownType(typeName)) {
820821
errorMessage +=
821822
String.format(
822823
" Ensure that the descriptor for type '%s' was added to the environment", typeName);
@@ -858,7 +859,9 @@ private static CelType normalizeFieldType(CelType celType) {
858859
/** TODO: Remove after cl/984117942 is submitted. */
859860
private static Optional<CelType> lookupLegacyFieldType(
860861
TypeProvider legacyTypeProvider, CelType type, String fieldName) {
861-
TypeProvider.FieldType legacyFieldType = legacyTypeProvider.lookupFieldType(type, fieldName);
862+
Type messageType = CelProtoTypes.createMessage(type.name());
863+
TypeProvider.FieldType legacyFieldType =
864+
legacyTypeProvider.lookupFieldType(messageType, fieldName);
862865
if (legacyFieldType != null) {
863866
return Optional.of(legacyFieldType.celType());
864867
}

‎checker/src/test/java/dev/cel/checker/CelCheckerLegacyImplTest.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.common.collect.ImmutableMap;
2222
import com.google.protobuf.Duration;
2323
import com.google.protobuf.FieldMask;
24+
import com.google.protobuf.Timestamp;
2425
import com.google.testing.junit.testparameterinjector.TestParameter;
2526
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
2627
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
@@ -182,6 +183,20 @@ public void check_wellKnownTypeStructCreation_withLegacyTypeProvider_success() t
182183
assertThat(ast.getResultType()).isEqualTo(SimpleType.DURATION);
183184
}
184185

186+
@Test
187+
public void check_wellKnownTypeTimestampStructCreation_withLegacyTypeProvider_success()
188+
throws Exception {
189+
TypeProvider legacyTypeProvider =
190+
new DescriptorTypeProvider(ImmutableList.of(Timestamp.getDescriptor()));
191+
CelCompiler celCompiler =
192+
CelCompilerFactory.standardCelCompilerBuilder().setTypeProvider(legacyTypeProvider).build();
193+
194+
CelAbstractSyntaxTree ast =
195+
celCompiler.compile("google.protobuf.Timestamp{seconds: 100, nanos: 200}").getAst();
196+
197+
assertThat(ast.getResultType()).isEqualTo(SimpleType.TIMESTAMP);
198+
}
199+
185200
@Test
186201
public void check_protoTypeMask_failsClosedWithLegacyTypeProvider() throws Exception {
187202
TypeProvider legacyTypeProvider =

‎checker/src/test/java/dev/cel/checker/CelStandardDeclarationsTest.java‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ public void standardDeclarations_includeIdentifiers() {
209209
.containsExactly(StandardIdentifier.INT.identDecl(), StandardIdentifier.UINT.identDecl());
210210
}
211211

212+
@Test
213+
public void standardDeclarations_includeDurationAndTimestampIdentifiers() {
214+
CelStandardDeclarations celStandardDeclaration =
215+
CelStandardDeclarations.newBuilder()
216+
.includeIdentifiers(StandardIdentifier.DURATION, StandardIdentifier.TIMESTAMP)
217+
.build();
218+
219+
assertThat(celStandardDeclaration.identifierDecls())
220+
.containsExactly(
221+
StandardIdentifier.DURATION.identDecl(), StandardIdentifier.TIMESTAMP.identDecl());
222+
}
223+
212224
@Test
213225
public void standardDeclarations_excludeIdentifiers() {
214226
CelStandardDeclarations celStandardDeclaration =
@@ -222,6 +234,45 @@ public void standardDeclarations_excludeIdentifiers() {
222234
.doesNotContain(StandardIdentifier.UINT.identDecl());
223235
}
224236

237+
@Test
238+
public void standardEnvironment_excludeDurationIdentifier_compilationFails() {
239+
CelCompiler celCompiler =
240+
CelCompilerFactory.standardCelCompilerBuilder()
241+
.setStandardDeclarations(
242+
CelStandardDeclarations.newBuilder()
243+
.excludeIdentifiers(StandardIdentifier.DURATION)
244+
.build())
245+
.build();
246+
247+
CelValidationException e =
248+
assertThrows(
249+
CelValidationException.class,
250+
() -> celCompiler.compile("google.protobuf.Duration == type(duration('1h'))").getAst());
251+
252+
assertThat(e).hasMessageThat().contains("undeclared reference to 'google'");
253+
}
254+
255+
@Test
256+
public void standardEnvironment_excludeTimestampIdentifier_compilationFails() {
257+
CelCompiler celCompiler =
258+
CelCompilerFactory.standardCelCompilerBuilder()
259+
.setStandardDeclarations(
260+
CelStandardDeclarations.newBuilder()
261+
.excludeIdentifiers(StandardIdentifier.TIMESTAMP)
262+
.build())
263+
.build();
264+
265+
CelValidationException e =
266+
assertThrows(
267+
CelValidationException.class,
268+
() ->
269+
celCompiler
270+
.compile("google.protobuf.Timestamp == type(timestamp('2023-01-01T00:00:00Z'))")
271+
.getAst());
272+
273+
assertThat(e).hasMessageThat().contains("undeclared reference to 'google'");
274+
}
275+
225276
@Test
226277
public void standardDeclarations_filterIdentifiers() {
227278
CelStandardDeclarations celStandardDeclaration =

‎checker/src/test/java/dev/cel/checker/ExprCheckerTest.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,10 @@ public void types() throws Exception {
812812
runTest();
813813
source = "{}.map(c,[c,type(c)])";
814814
runTest();
815+
source =
816+
"google.protobuf.Duration == type(duration('1h')) "
817+
+ "&& google.protobuf.Timestamp == type(timestamp(0))";
818+
runTest();
815819
}
816820

817821
// Enum Values

‎checker/src/test/java/dev/cel/checker/TypesTest.java‎

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,19 @@
1515
package dev.cel.checker;
1616

1717
import static com.google.common.truth.Truth.assertThat;
18+
import static org.junit.Assert.assertThrows;
1819

1920
import dev.cel.expr.Type;
2021
import dev.cel.expr.Type.PrimitiveType;
22+
import com.google.protobuf.Duration;
23+
import com.google.protobuf.Timestamp;
24+
import com.google.testing.junit.testparameterinjector.TestParameter;
25+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
2126
import dev.cel.common.CelAbstractSyntaxTree;
27+
import dev.cel.common.CelContainer;
2228
import dev.cel.common.CelFunctionDecl;
2329
import dev.cel.common.CelOverloadDecl;
30+
import dev.cel.common.CelValidationException;
2431
import dev.cel.common.types.CelKind;
2532
import dev.cel.common.types.CelProtoTypes;
2633
import dev.cel.common.types.CelType;
@@ -37,9 +44,8 @@
3744
import java.util.Map;
3845
import org.junit.Test;
3946
import org.junit.runner.RunWith;
40-
import org.junit.runners.JUnit4;
4147

42-
@RunWith(JUnit4.class)
48+
@RunWith(TestParameterInjector.class)
4349
public class TypesTest {
4450

4551
@Test
@@ -350,6 +356,215 @@ public void compiler_typeParamInTypeType_resolvesReturnTypeString() throws Excep
350356
assertThat(ast.getResultType()).isEqualTo(SimpleType.STRING);
351357
}
352358

359+
private enum WellKnownTypeIdentTestCase {
360+
DURATION_QUALIFIED("google.protobuf.Duration", TypeType.create(SimpleType.DURATION)),
361+
DURATION_LEADING_DOT(".google.protobuf.Duration", TypeType.create(SimpleType.DURATION)),
362+
DURATION_UNQUALIFIED("Duration", TypeType.create(SimpleType.DURATION)),
363+
TIMESTAMP_QUALIFIED("google.protobuf.Timestamp", TypeType.create(SimpleType.TIMESTAMP)),
364+
TIMESTAMP_LEADING_DOT(".google.protobuf.Timestamp", TypeType.create(SimpleType.TIMESTAMP)),
365+
TIMESTAMP_UNQUALIFIED("Timestamp", TypeType.create(SimpleType.TIMESTAMP));
366+
367+
private final String expression;
368+
private final CelType expectedType;
369+
370+
WellKnownTypeIdentTestCase(String expression, CelType expectedType) {
371+
this.expression = expression;
372+
this.expectedType = expectedType;
373+
}
374+
}
375+
376+
@Test
377+
public void compiler_wellKnownProtoTypeIdent_resolvesToSimpleType(
378+
@TestParameter WellKnownTypeIdentTestCase testCase) throws Exception {
379+
CelCompiler celCompiler =
380+
CelCompilerFactory.standardCelCompilerBuilder()
381+
.addMessageTypes(Duration.getDescriptor(), Timestamp.getDescriptor())
382+
.setContainer(CelContainer.ofName("google.protobuf"))
383+
.build();
384+
385+
CelAbstractSyntaxTree ast = celCompiler.compile(testCase.expression).getAst();
386+
387+
assertThat(ast.getResultType()).isEqualTo(testCase.expectedType);
388+
}
389+
390+
private enum WellKnownTypeParamTestCase {
391+
DURATION_QUALIFIED_CAST("cast('1h', google.protobuf.Duration)", SimpleType.DURATION),
392+
DURATION_QUALIFIED_EQUALITY(
393+
"cast('1h', google.protobuf.Duration) == duration('1h')", SimpleType.BOOL),
394+
DURATION_UNQUALIFIED_COMPARISON("cast('1h', Duration) > duration('0s')", SimpleType.BOOL),
395+
TIMESTAMP_QUALIFIED_CAST("cast(0, google.protobuf.Timestamp)", SimpleType.TIMESTAMP),
396+
TIMESTAMP_QUALIFIED_EQUALITY(
397+
"cast(0, google.protobuf.Timestamp) == timestamp(0)", SimpleType.BOOL),
398+
TIMESTAMP_UNQUALIFIED_COMPARISON("cast(0, Timestamp) > timestamp(0)", SimpleType.BOOL);
399+
400+
private final String expression;
401+
private final CelType expectedType;
402+
403+
WellKnownTypeParamTestCase(String expression, CelType expectedType) {
404+
this.expression = expression;
405+
this.expectedType = expectedType;
406+
}
407+
}
408+
409+
@Test
410+
public void compiler_typeParamInTypeType_withWellKnownProto_resolvesWellKnownOverloads(
411+
@TestParameter WellKnownTypeParamTestCase testCase) throws Exception {
412+
TypeParamType typeParamT = TypeParamType.create("T");
413+
CelCompiler celCompiler =
414+
CelCompilerFactory.standardCelCompilerBuilder()
415+
.addMessageTypes(Duration.getDescriptor(), Timestamp.getDescriptor())
416+
.setContainer(CelContainer.ofName("google.protobuf"))
417+
.addFunctionDeclarations(
418+
CelFunctionDecl.newFunctionDeclaration(
419+
"cast",
420+
CelOverloadDecl.newGlobalOverload(
421+
"cast_t", typeParamT, SimpleType.DYN, TypeType.create(typeParamT))))
422+
.build();
423+
424+
CelAbstractSyntaxTree ast = celCompiler.compile(testCase.expression).getAst();
425+
426+
assertThat(ast.getResultType()).isEqualTo(testCase.expectedType);
427+
}
428+
429+
@Test
430+
public void compiler_durationIdent_withoutMessageTypes_resolvesToSimpleType() throws Exception {
431+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
432+
433+
CelAbstractSyntaxTree ast = celCompiler.compile("google.protobuf.Duration").getAst();
434+
435+
assertThat(ast.getResultType()).isEqualTo(TypeType.create(SimpleType.DURATION));
436+
}
437+
438+
@Test
439+
public void compiler_timestampIdent_withoutMessageTypes_resolvesToSimpleType() throws Exception {
440+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
441+
442+
CelAbstractSyntaxTree ast = celCompiler.compile("google.protobuf.Timestamp").getAst();
443+
444+
assertThat(ast.getResultType()).isEqualTo(TypeType.create(SimpleType.TIMESTAMP));
445+
}
446+
447+
@Test
448+
public void compiler_durationStructCreation_withDescriptor_success() throws Exception {
449+
CelCompiler celCompiler =
450+
CelCompilerFactory.standardCelCompilerBuilder()
451+
.addMessageTypes(Duration.getDescriptor())
452+
.build();
453+
454+
CelAbstractSyntaxTree ast =
455+
celCompiler.compile("google.protobuf.Duration{seconds: 10, nanos: 20}").getAst();
456+
457+
assertThat(ast.getResultType()).isEqualTo(SimpleType.DURATION);
458+
}
459+
460+
@Test
461+
public void compiler_timestampStructCreation_withDescriptor_success() throws Exception {
462+
CelCompiler celCompiler =
463+
CelCompilerFactory.standardCelCompilerBuilder()
464+
.addMessageTypes(Timestamp.getDescriptor())
465+
.build();
466+
467+
CelAbstractSyntaxTree ast =
468+
celCompiler.compile("google.protobuf.Timestamp{seconds: 100, nanos: 200}").getAst();
469+
470+
assertThat(ast.getResultType()).isEqualTo(SimpleType.TIMESTAMP);
471+
}
472+
473+
@Test
474+
public void compiler_durationStructCreation_emptyFields_success() throws Exception {
475+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
476+
477+
CelAbstractSyntaxTree ast = celCompiler.compile("google.protobuf.Duration{}").getAst();
478+
479+
assertThat(ast.getResultType()).isEqualTo(SimpleType.DURATION);
480+
}
481+
482+
@Test
483+
public void compiler_timestampStructCreation_emptyFields_success() throws Exception {
484+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
485+
486+
CelAbstractSyntaxTree ast = celCompiler.compile("google.protobuf.Timestamp{}").getAst();
487+
488+
assertThat(ast.getResultType()).isEqualTo(SimpleType.TIMESTAMP);
489+
}
490+
491+
@Test
492+
public void compiler_durationStructCreation_withoutDescriptor_throws() {
493+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
494+
495+
CelValidationException e =
496+
assertThrows(
497+
CelValidationException.class,
498+
() -> celCompiler.compile("google.protobuf.Duration{seconds: 10}").getAst());
499+
500+
assertThat(e)
501+
.hasMessageThat()
502+
.contains(
503+
"Message type resolution failure while referencing field 'seconds'."
504+
+ " Ensure that the descriptor for type 'google.protobuf.Duration' was added to the"
505+
+ " environment");
506+
}
507+
508+
@Test
509+
public void compiler_timestampStructCreation_withoutDescriptor_throws() {
510+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
511+
512+
CelValidationException e =
513+
assertThrows(
514+
CelValidationException.class,
515+
() -> celCompiler.compile("google.protobuf.Timestamp{seconds: 10}").getAst());
516+
517+
assertThat(e)
518+
.hasMessageThat()
519+
.contains(
520+
"Message type resolution failure while referencing field 'seconds'. Ensure that the"
521+
+ " descriptor for type 'google.protobuf.Timestamp' was added to the environment");
522+
}
523+
524+
@Test
525+
public void compiler_durationStructCreation_typeMismatch_throws() {
526+
CelCompiler celCompiler =
527+
CelCompilerFactory.standardCelCompilerBuilder()
528+
.addMessageTypes(Duration.getDescriptor())
529+
.build();
530+
531+
CelValidationException e =
532+
assertThrows(
533+
CelValidationException.class,
534+
() -> celCompiler.compile("google.protobuf.Duration{seconds: 'bad'}").getAst());
535+
536+
assertThat(e)
537+
.hasMessageThat()
538+
.contains("expected type of field 'seconds' is 'int' but provided type is 'string'");
539+
}
540+
541+
@Test
542+
public void compiler_timestampStructCreation_typeMismatch_throws() {
543+
CelCompiler celCompiler =
544+
CelCompilerFactory.standardCelCompilerBuilder()
545+
.addMessageTypes(Timestamp.getDescriptor())
546+
.build();
547+
548+
CelValidationException e =
549+
assertThrows(
550+
CelValidationException.class,
551+
() -> celCompiler.compile("google.protobuf.Timestamp{seconds: 'bad'}").getAst());
552+
553+
assertThat(e)
554+
.hasMessageThat()
555+
.contains("expected type of field 'seconds' is 'int' but provided type is 'string'");
556+
}
557+
558+
@Test
559+
public void compiler_structCreation_primitiveType_throws() {
560+
CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder().build();
561+
562+
CelValidationException e =
563+
assertThrows(CelValidationException.class, () -> celCompiler.compile("int{}").getAst());
564+
565+
assertThat(e).hasMessageThat().contains("'int' is not a message type");
566+
}
567+
353568
@Test
354569
public void compiler_typeParamInCompositeTypeType_resolvesReturnType() throws Exception {
355570
TypeParamType typeParamT = TypeParamType.create("T");

0 commit comments

Comments
 (0)