mod common;
use common::run_lua;
#[tokio::test]
async fn workflow_coroutine_can_call_os_date_and_register_query() {
let script = r#"
-- Simulate the ctx the worker hands to a workflow function.
-- register_query in the real ctx stores into _query_handlers; the
-- shape mirrors stdlib/engine/workflow/ctx.lua.
local ctx = {
_query_handlers = {},
}
function ctx:register_query(name, fn)
self._query_handlers[name] = fn
end
-- This is the exact pattern from the issue: a workflow function
-- that calls register_query and os.date on first run.
local function workflow(c, input)
c:register_query("state", function() return { ok = true } end)
local now = os.date("!%Y-%m-%dT%H:%M:%SZ")
return { now = now, input = input }
end
local co = coroutine.create(function() return workflow(ctx, { hello = true }) end)
local ok, result = coroutine.resume(co)
assert.eq(ok, true)
assert.eq(coroutine.status(co), "dead")
assert.eq(type(result), "table")
assert.eq(result.input.hello, true)
assert.eq(result.now ~= nil and #result.now > 0, true)
assert.not_nil(ctx._query_handlers.state)
-- Query handler is callable from outside the coroutine.
local snapshot = ctx._query_handlers.state()
assert.eq(snapshot.ok, true)
"#;
run_lua(script).await.unwrap();
}
#[tokio::test]
async fn coroutine_can_yield_and_resume_with_args() {
let script = r#"
local function workflow()
local first = coroutine.yield({ type = "Step1" })
local second = coroutine.yield({ type = "Step2", got = first })
return { final = second }
end
local co = coroutine.create(workflow)
local ok, cmd1 = coroutine.resume(co)
assert.eq(ok, true)
assert.eq(cmd1.type, "Step1")
local ok2, cmd2 = coroutine.resume(co, "alpha")
assert.eq(ok2, true)
assert.eq(cmd2.type, "Step2")
assert.eq(cmd2.got, "alpha")
local ok3, result = coroutine.resume(co, "beta")
assert.eq(ok3, true)
assert.eq(result.final, "beta")
assert.eq(coroutine.status(co), "dead")
"#;
run_lua(script).await.unwrap();
}