diff --git a/command.ts b/command.ts index 9512576c..1ab81d67 100644 --- a/command.ts +++ b/command.ts @@ -117,6 +117,18 @@ export type StralgoTarget = "KEYS" | "STRINGS"; export interface SetOpts { ex?: number; px?: number; + /** + * Sets `EXAT` option. + * + * `EXAT` option was added in Redis v6.2. + */ + exat?: number; + /** + * Sets `PXAT` option. + * + * `PXAT` option was added in Redis v6.2. + */ + pxat?: number; keepttl?: boolean; /** * Enables `GET` option. diff --git a/redis.ts b/redis.ts index dfc1f51d..16397104 100644 --- a/redis.ts +++ b/redis.ts @@ -1364,14 +1364,20 @@ class RedisImpl implements Redis { | SetWithModeOpts, ) { const args: RedisValue[] = [key, value]; - if (opts?.ex !== undefined) { + if (opts?.ex != null) { args.push("EX", opts.ex); - } else if (opts?.px !== undefined) { + } else if (opts?.px != null) { args.push("PX", opts.px); + } else if (opts?.exat != null) { + args.push("EXAT", opts.exat); + } else if (opts?.pxat != null) { + args.push("PXAT", opts.pxat); } + // TODO: Isn't `KEEPTTL` option exclusive with `EX`, `PX`, etc.? if (opts?.keepttl) { args.push("KEEPTTL"); } + let isAbleToReturnNil = false; if ((opts as SetWithModeOpts)?.mode) { args.push((opts as SetWithModeOpts).mode); diff --git a/tests/commands/string.ts b/tests/commands/string.ts index 18840807..4ed1d132 100644 --- a/tests/commands/string.ts +++ b/tests/commands/string.ts @@ -1,4 +1,8 @@ -import { assertEquals } from "../../deps/std/assert.ts"; +import { + assertEquals, + assertGreater, + assertLessOrEqual, +} from "../../deps/std/assert.ts"; import type { IsExact, IsNullable } from "../../deps/std/testing.ts"; import { afterAll, @@ -8,7 +12,11 @@ import { describe, it, } from "../../deps/std/testing.ts"; -import type { Connector, TestServer } from "../test_util.ts"; +import { + type Connector, + type TestServer, + usesRedisVersion, +} from "../test_util.ts"; import type { Redis } from "../../mod.ts"; export function stringTests( @@ -205,6 +213,28 @@ export function stringTests( assertEquals(v2, null); assertType>(true); }); + + it("supports `EXAT` option", async () => { + const date = new Date(); + date.setDate(date.getDate() + 1); + await client.set("setWithEXAT", "foo", { + exat: Math.floor(date.valueOf() / 1000), + }); + const ttl = await client.ttl("setWithEXAT"); + const oneDay = 86400; + assertLessOrEqual(ttl, oneDay); + assertGreater(ttl, oneDay - 3); + }); + + it("supports `PXAT` option", async () => { + const date = new Date(); + date.setDate(date.getDate() + 1); + await client.set("setWithPXAT", "bar", { pxat: date.valueOf() }); + const ttl = await client.ttl("setWithPXAT"); + const oneDay = 86400; + assertLessOrEqual(ttl, oneDay); + assertGreater(ttl, oneDay - 3); + }); }); it("setbit", async () => { @@ -244,7 +274,7 @@ export function stringTests( it("stralgo", { // NOTE(#454): STRALGO has been dropped - ignore: !Deno.env.get("REDIS_VERSION")?.startsWith("6."), + ignore: !usesRedisVersion("6"), }, async () => { await client.set("a", "Hello"); await client.set("b", "Deno!"); diff --git a/tests/test_util.ts b/tests/test_util.ts index ae084cbe..b55109e6 100644 --- a/tests/test_util.ts +++ b/tests/test_util.ts @@ -117,3 +117,7 @@ function tempPath(fileName: string): string { const url = new URL(`./tmp/${fileName}`, import.meta.url); return url.pathname; } + +export function usesRedisVersion(version: "6" | "7"): boolean { + return !!Deno.env.get("REDIS_VERSION")?.startsWith(`${version}.`); +}