Skip to content

Commit 50d65a1

Browse files
committed
Match Redis behavior: TTL <= 0 means never expire, add comprehensive tests
1 parent e01dcdf commit 50d65a1

File tree

3 files changed

+223
-217
lines changed

3 files changed

+223
-217
lines changed

state/aws/dynamodb/dynamodb.go

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -487,26 +487,7 @@ func (d *StateStore) Multi(ctx context.Context, request *state.TransactionalStat
487487
if err != nil {
488488
return fmt.Errorf("dynamodb error: failed to marshal value for key %s: %w", req.Key, err)
489489
}
490-
ttl, err := d.parseTTL(&req)
491-
if err != nil {
492-
return fmt.Errorf("dynamodb error: failed to parse ttlInSeconds: %w", err)
493-
}
494-
twi.Put = &types.Put{
495-
TableName: ptr.Of(d.table),
496-
Item: map[string]types.AttributeValue{
497-
d.partitionKey: &types.AttributeValueMemberS{
498-
Value: req.Key,
499-
},
500-
"value": &types.AttributeValueMemberS{
501-
Value: value,
502-
},
503-
},
504-
}
505-
if ttl != nil {
506-
twi.Put.Item[d.ttlAttributeName] = &types.AttributeValueMemberN{
507-
Value: strconv.FormatInt(*ttl, 10),
508-
}
509-
}
490+
twi.Put = pd.ToPut()
510491

511492
case state.DeleteRequest:
512493
twi.Delete = &types.Delete{

state/aws/dynamodb/dynamodb_test.go

Lines changed: 0 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,201 +1205,4 @@ func TestMultiTx(t *testing.T) {
12051205
err := s.Multi(t.Context(), req)
12061206
require.NoError(t, err)
12071207
})
1208-
}
1209-
1210-
func TestParseTTLWithDefault(t *testing.T) {
1211-
t.Run("Use explicit TTL from request metadata", func(t *testing.T) {
1212-
defaultTTL := 600
1213-
s := StateStore{
1214-
ttlAttributeName: "expiresAt",
1215-
ttlInSeconds: &defaultTTL,
1216-
}
1217-
1218-
req := &state.SetRequest{
1219-
Key: "test-key",
1220-
Metadata: map[string]string{
1221-
"ttlInSeconds": "300",
1222-
},
1223-
}
1224-
1225-
ttl, err := s.parseTTL(req)
1226-
require.NoError(t, err)
1227-
require.NotNil(t, ttl)
1228-
1229-
// Should use explicit value (300), not default (600)
1230-
expectedTime := time.Now().Unix() + 300
1231-
assert.InDelta(t, expectedTime, *ttl, 2) // Allow 2 second tolerance
1232-
})
1233-
1234-
t.Run("Use default TTL when no explicit TTL in request", func(t *testing.T) {
1235-
defaultTTL := 600
1236-
s := StateStore{
1237-
ttlAttributeName: "expiresAt",
1238-
ttlInSeconds: &defaultTTL,
1239-
}
1240-
1241-
req := &state.SetRequest{
1242-
Key: "test-key",
1243-
Metadata: map[string]string{},
1244-
}
1245-
1246-
ttl, err := s.parseTTL(req)
1247-
require.NoError(t, err)
1248-
require.NotNil(t, ttl)
1249-
1250-
// Should use default value (600)
1251-
expectedTime := time.Now().Unix() + 600
1252-
assert.InDelta(t, expectedTime, *ttl, 2) // Allow 2 second tolerance
1253-
})
1254-
1255-
t.Run("No TTL when no default and no explicit TTL", func(t *testing.T) {
1256-
s := StateStore{
1257-
ttlAttributeName: "expiresAt",
1258-
ttlInSeconds: nil, // No default configured
1259-
}
1260-
1261-
req := &state.SetRequest{
1262-
Key: "test-key",
1263-
Metadata: map[string]string{},
1264-
}
1265-
1266-
ttl, err := s.parseTTL(req)
1267-
require.NoError(t, err)
1268-
assert.Nil(t, ttl)
1269-
})
1270-
1271-
t.Run("No TTL when ttlAttributeName is not set", func(t *testing.T) {
1272-
defaultTTL := 600
1273-
s := StateStore{
1274-
ttlAttributeName: "", // TTL not enabled in component
1275-
ttlInSeconds: &defaultTTL,
1276-
}
1277-
1278-
req := &state.SetRequest{
1279-
Key: "test-key",
1280-
Metadata: map[string]string{
1281-
"ttlInSeconds": "300",
1282-
},
1283-
}
1284-
1285-
ttl, err := s.parseTTL(req)
1286-
require.NoError(t, err)
1287-
assert.Nil(t, ttl) // Should return nil when TTL not enabled
1288-
})
1289-
1290-
t.Run("Explicit TTL with value -1 means no expiration", func(t *testing.T) {
1291-
defaultTTL := 600
1292-
s := StateStore{
1293-
ttlAttributeName: "expiresAt",
1294-
ttlInSeconds: &defaultTTL,
1295-
}
1296-
1297-
req := &state.SetRequest{
1298-
Key: "test-key",
1299-
Metadata: map[string]string{
1300-
"ttlInSeconds": "-1",
1301-
},
1302-
}
1303-
1304-
ttl, err := s.parseTTL(req)
1305-
require.NoError(t, err)
1306-
// -1 means never expire
1307-
assert.Nil(t, ttl)
1308-
})
1309-
1310-
t.Run("Default TTL with large value", func(t *testing.T) {
1311-
defaultTTL := 86400 // 24 hours
1312-
s := StateStore{
1313-
ttlAttributeName: "expiresAt",
1314-
ttlInSeconds: &defaultTTL,
1315-
}
1316-
1317-
req := &state.SetRequest{
1318-
Key: "test-key",
1319-
Metadata: map[string]string{},
1320-
}
1321-
1322-
ttl, err := s.parseTTL(req)
1323-
require.NoError(t, err)
1324-
require.NotNil(t, ttl)
1325-
1326-
expectedTime := time.Now().Unix() + 86400
1327-
assert.InDelta(t, expectedTime, *ttl, 2)
1328-
})
1329-
1330-
t.Run("Error on invalid TTL value", func(t *testing.T) {
1331-
defaultTTL := 600
1332-
s := StateStore{
1333-
ttlAttributeName: "expiresAt",
1334-
ttlInSeconds: &defaultTTL,
1335-
}
1336-
1337-
req := &state.SetRequest{
1338-
Key: "test-key",
1339-
Metadata: map[string]string{
1340-
"ttlInSeconds": "invalid",
1341-
},
1342-
}
1343-
1344-
ttl, err := s.parseTTL(req)
1345-
require.Error(t, err)
1346-
assert.Nil(t, ttl)
1347-
assert.Contains(t, err.Error(), "invalid syntax")
1348-
})
1349-
1350-
t.Run("Explicit TTL with value 0 means no expiration", func(t *testing.T) {
1351-
defaultTTL := 1200
1352-
s := StateStore{
1353-
ttlAttributeName: "expiresAt",
1354-
ttlInSeconds: &defaultTTL,
1355-
}
1356-
1357-
req := &state.SetRequest{
1358-
Key: "test-key",
1359-
Metadata: map[string]string{
1360-
"ttlInSeconds": "0",
1361-
},
1362-
}
1363-
1364-
ttl, err := s.parseTTL(req)
1365-
require.NoError(t, err)
1366-
// 0 means never expire, overriding default
1367-
assert.Nil(t, ttl)
1368-
})
1369-
1370-
t.Run("Default TTL with value 0 means no expiration", func(t *testing.T) {
1371-
defaultTTL := 0
1372-
s := StateStore{
1373-
ttlAttributeName: "expiresAt",
1374-
ttlInSeconds: &defaultTTL,
1375-
}
1376-
1377-
req := &state.SetRequest{
1378-
Key: "test-key",
1379-
Metadata: map[string]string{},
1380-
}
1381-
1382-
ttl, err := s.parseTTL(req)
1383-
require.NoError(t, err)
1384-
// Default of 0 means never expire
1385-
assert.Nil(t, ttl)
1386-
})
1387-
1388-
t.Run("Default TTL with negative value means no expiration", func(t *testing.T) {
1389-
defaultTTL := -1
1390-
s := StateStore{
1391-
ttlAttributeName: "expiresAt",
1392-
ttlInSeconds: &defaultTTL,
1393-
}
1394-
1395-
req := &state.SetRequest{
1396-
Key: "test-key",
1397-
Metadata: map[string]string{},
1398-
}
1399-
1400-
ttl, err := s.parseTTL(req)
1401-
require.NoError(t, err)
1402-
// Default of -1 means never expire
1403-
assert.Nil(t, ttl)
1404-
})
14051208
}

0 commit comments

Comments
 (0)