1111import com .google .protobuf .Timestamp ;
1212import feast .core .FeatureSetProto .EntitySpec ;
1313import feast .core .FeatureSetProto .FeatureSetSpec ;
14+ import feast .serving .ServingAPIProto .FeatureSetRequest ;
1415import feast .serving .ServingAPIProto .GetOnlineFeaturesRequest ;
1516import feast .serving .ServingAPIProto .GetOnlineFeaturesRequest .EntityRow ;
16- import feast .serving .ServingAPIProto .FeatureSetRequest ;
1717import feast .serving .ServingAPIProto .GetOnlineFeaturesResponse ;
1818import feast .serving .ServingAPIProto .GetOnlineFeaturesResponse .FieldValues ;
1919import feast .storage .RedisProto .RedisKey ;
@@ -53,14 +53,6 @@ public class RedisServingServiceTest {
5353 @ Before
5454 public void setUp () {
5555 initMocks (this );
56- FeatureSetSpec featureSetSpec = FeatureSetSpec .newBuilder ()
57- .addEntities (EntitySpec .newBuilder ().setName ("entity1" ))
58- .addEntities (EntitySpec .newBuilder ().setName ("entity2" ))
59- .setMaxAge (Duration .newBuilder ().setSeconds (30 )) // default
60- .build ();
61-
62- when (specService .getFeatureSet ("featureSet" , 1 ))
63- .thenReturn (featureSetSpec );
6456
6557 redisServingService = new RedisServingService (jedisPool , specService , tracer );
6658 redisKeyList = Lists .newArrayList (
@@ -124,6 +116,72 @@ public void shouldReturnResponseWithValuesIfKeysPresent() {
124116 List <byte []> featureRowBytes = featureRows .stream ()
125117 .map (AbstractMessageLite ::toByteArray )
126118 .collect (Collectors .toList ());
119+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpec ());
120+ when (jedisPool .getResource ()).thenReturn (jedis );
121+ when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
122+ when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
123+
124+ GetOnlineFeaturesResponse expected = GetOnlineFeaturesResponse .newBuilder ()
125+ .addFieldValues (FieldValues .newBuilder ()
126+ .putFields ("entity1" , intValue (1 ))
127+ .putFields ("entity2" , strValue ("a" ))
128+ .putFields ("featureSet:1:feature1" , intValue (1 ))
129+ .putFields ("featureSet:1:feature2" , intValue (1 )))
130+ .addFieldValues (FieldValues .newBuilder ()
131+ .putFields ("entity1" , intValue (2 ))
132+ .putFields ("entity2" , strValue ("b" ))
133+ .putFields ("featureSet:1:feature1" , intValue (2 ))
134+ .putFields ("featureSet:1:feature2" , intValue (2 )))
135+ .build ();
136+ GetOnlineFeaturesResponse actual = redisServingService .getOnlineFeatures (request );
137+ assertThat (responseToMapList (actual ), containsInAnyOrder (responseToMapList (expected ).toArray ()));
138+ }
139+
140+ @ Test
141+ public void shouldReturnResponseWithValuesWhenFeatureSetSpecHasUnspecifiedMaxAge () {
142+ GetOnlineFeaturesRequest request = GetOnlineFeaturesRequest .newBuilder ()
143+ .addFeatureSets (FeatureSetRequest .newBuilder ()
144+ .setName ("featureSet" )
145+ .setVersion (1 )
146+ .addAllFeatureNames (Lists .newArrayList ("feature1" , "feature2" ))
147+ .build ())
148+ .addEntityRows (EntityRow .newBuilder ()
149+ .setEntityTimestamp (Timestamp .newBuilder ().setSeconds (100 ))
150+ .putFields ("entity1" , intValue (1 ))
151+ .putFields ("entity2" , strValue ("a" )))
152+ .addEntityRows (EntityRow .newBuilder ()
153+ .setEntityTimestamp (Timestamp .newBuilder ().setSeconds (100 ))
154+ .putFields ("entity1" , intValue (2 ))
155+ .putFields ("entity2" , strValue ("b" )))
156+ .build ();
157+
158+ List <FeatureRow > featureRows = Lists .newArrayList (
159+ FeatureRow .newBuilder ()
160+ .setEventTimestamp (Timestamp .newBuilder ().setSeconds (2 )) // much older timestamp
161+ .addAllFields (Lists
162+ .newArrayList (
163+ Field .newBuilder ().setName ("entity1" ).setValue (intValue (1 )).build (),
164+ Field .newBuilder ().setName ("entity2" ).setValue (strValue ("a" )).build (),
165+ Field .newBuilder ().setName ("feature1" ).setValue (intValue (1 )).build (),
166+ Field .newBuilder ().setName ("feature2" ).setValue (intValue (1 )).build ()))
167+ .setFeatureSet ("featureSet:1" )
168+ .build (),
169+ FeatureRow .newBuilder ()
170+ .setEventTimestamp (Timestamp .newBuilder ().setSeconds (15 )) // much older timestamp
171+ .addAllFields (Lists
172+ .newArrayList (
173+ Field .newBuilder ().setName ("entity1" ).setValue (intValue (2 )).build (),
174+ Field .newBuilder ().setName ("entity2" ).setValue (strValue ("b" )).build (),
175+ Field .newBuilder ().setName ("feature1" ).setValue (intValue (2 )).build (),
176+ Field .newBuilder ().setName ("feature2" ).setValue (intValue (2 )).build ()))
177+ .setFeatureSet ("featureSet:1" )
178+ .build ()
179+ );
180+
181+ List <byte []> featureRowBytes = featureRows .stream ()
182+ .map (AbstractMessageLite ::toByteArray )
183+ .collect (Collectors .toList ());
184+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpecWithNoMaxAge ());
127185 when (jedisPool .getResource ()).thenReturn (jedis );
128186 when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
129187 when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
@@ -187,6 +245,7 @@ public void shouldReturnResponseWithUnsetValuesIfKeysNotPresent() {
187245 );
188246
189247 List <byte []> featureRowBytes = Lists .newArrayList (featureRows .get (0 ).toByteArray (), null );
248+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpec ());
190249 when (jedisPool .getResource ()).thenReturn (jedis );
191250 when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
192251 when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
@@ -253,6 +312,7 @@ public void shouldReturnResponseWithUnsetValuesIfMaxAgeIsExceeded() {
253312 List <byte []> featureRowBytes = featureRows .stream ()
254313 .map (AbstractMessageLite ::toByteArray )
255314 .collect (Collectors .toList ());
315+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpec ());
256316 when (jedisPool .getResource ()).thenReturn (jedis );
257317 when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
258318 when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
@@ -319,6 +379,7 @@ public void shouldReturnResponseWithUnsetValuesIfDefaultMaxAgeIsExceeded() {
319379 List <byte []> featureRowBytes = featureRows .stream ()
320380 .map (AbstractMessageLite ::toByteArray )
321381 .collect (Collectors .toList ());
382+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpec ());
322383 when (jedisPool .getResource ()).thenReturn (jedis );
323384 when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
324385 when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
@@ -385,6 +446,7 @@ public void shouldFilterOutUndesiredRows() {
385446 List <byte []> featureRowBytes = featureRows .stream ()
386447 .map (AbstractMessageLite ::toByteArray )
387448 .collect (Collectors .toList ());
449+ when (specService .getFeatureSet ("featureSet" , 1 )).thenReturn (getFeatureSetSpec ());
388450 when (jedisPool .getResource ()).thenReturn (jedis );
389451 when (jedis .mget (redisKeyList )).thenReturn (featureRowBytes );
390452 when (tracer .buildSpan (ArgumentMatchers .any ())).thenReturn (Mockito .mock (SpanBuilder .class ));
@@ -414,4 +476,20 @@ private Value intValue(int val) {
414476 private Value strValue (String val ) {
415477 return Value .newBuilder ().setStringVal (val ).build ();
416478 }
479+ private FeatureSetSpec getFeatureSetSpec () {
480+ return FeatureSetSpec .newBuilder ()
481+ .addEntities (EntitySpec .newBuilder ().setName ("entity1" ))
482+ .addEntities (EntitySpec .newBuilder ().setName ("entity2" ))
483+ .setMaxAge (Duration .newBuilder ().setSeconds (30 )) // default
484+ .build ();
485+ }
486+
487+ private FeatureSetSpec getFeatureSetSpecWithNoMaxAge () {
488+ return FeatureSetSpec .newBuilder ()
489+ .addEntities (EntitySpec .newBuilder ().setName ("entity1" ))
490+ .addEntities (EntitySpec .newBuilder ().setName ("entity2" ))
491+ .setMaxAge (Duration .newBuilder ().setSeconds (0 ).setNanos (0 ).build ())
492+ .build ();
493+ }
494+
417495}
0 commit comments