Software /
code /
prosody
File
spec/util_promise_spec.lua @ 11590:5aafb832c91b
core.portmanager: Fix race condition in initialization of SNI cert map
Under some circumstances when hosts and modules are loaded in some
certain order, entries end up missing from the SNI map. This manifests
in e.g. `curl https://localhost:5281/` giving an error about
"unrecognized name".
The `service` argument is `nil` when invoked from the "host-activated"
event, leading it to iterating over every service. And then it would not
be fetching e.g. `http_host` from the config, which explains why https
would sometimes not work due to the missing name entry.
Because when `service` is included, this limits the iteration to
matching entries, while also returning the same value as the `name` loop
variable. Because `name == service when service != nil` we can use name
instead in the body of the loop.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 28 May 2021 17:09:22 +0200 |
parent | 11486:78d843faaffc |
child | 11727:f3aee8a825cc |
line wrap: on
line source
local promise = require "util.promise"; describe("util.promise", function () --luacheck: ignore 212/resolve 212/reject describe("new()", function () it("returns a promise object", function () assert(promise.new()); end); end); it("notifies immediately for fulfilled promises", function () local p = promise.new(function (resolve) resolve("foo"); end); local cb = spy.new(function (v) assert.equal("foo", v); end); p:next(cb); assert.spy(cb).was_called(1); end); it("notifies on fulfilment of pending promises", function () local r; local p = promise.new(function (resolve) r = resolve; end); local cb = spy.new(function (v) assert.equal("foo", v); end); p:next(cb); assert.spy(cb).was_called(0); r("foo"); assert.spy(cb).was_called(1); end); it("allows chaining :next() calls", function () local r; local result; local p = promise.new(function (resolve) r = resolve; end); local cb1 = spy.new(function (v) assert.equal("foo", v); return "bar"; end); local cb2 = spy.new(function (v) assert.equal("bar", v); result = v; end); p:next(cb1):next(cb2); assert.spy(cb1).was_called(0); assert.spy(cb2).was_called(0); r("foo"); assert.spy(cb1).was_called(1); assert.spy(cb2).was_called(1); assert.equal("bar", result); end); it("supports multiple :next() calls on the same promise", function () local r; local result; local p = promise.new(function (resolve) r = resolve; end); local cb1 = spy.new(function (v) assert.equal("foo", v); result = v; end); local cb2 = spy.new(function (v) assert.equal("foo", v); result = v; end); p:next(cb1); p:next(cb2); assert.spy(cb1).was_called(0); assert.spy(cb2).was_called(0); r("foo"); assert.spy(cb1).was_called(1); assert.spy(cb2).was_called(1); assert.equal("foo", result); end); it("automatically rejects on error", function () local r; local p = promise.new(function (resolve) r = resolve; error("oh no"); end); local cb = spy.new(function () end); local err_cb = spy.new(function (v) assert.equal("oh no", v); end); p:next(cb, err_cb); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(1); r("foo"); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(1); end); it("supports reject()", function () local r, result; local p = promise.new(function (resolve, reject) r = reject; end); local cb = spy.new(function () end); local err_cb = spy.new(function (v) result = v; assert.equal("oh doh", v); end); p:next(cb, err_cb); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(0); r("oh doh"); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(1); assert.equal("oh doh", result); end); it("supports chaining of rejected promises", function () local r, result; local p = promise.new(function (resolve, reject) r = reject; end); local cb = spy.new(function () end); local err_cb = spy.new(function (v) result = v; assert.equal("oh doh", v); return "ok" end); local cb2 = spy.new(function (v) result = v; end); local err_cb2 = spy.new(function () end); p:next(cb, err_cb):next(cb2, err_cb2) assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(0); assert.spy(cb2).was_called(0); assert.spy(err_cb2).was_called(0); r("oh doh"); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(err_cb2).was_called(0); assert.equal("ok", result); end); it("propagates errors down the chain, even when some handlers are not provided", function () local r, result; local test_error = {}; local p = promise.new(function (resolve, reject) r = reject; end); local cb = spy.new(function () end); local err_cb = spy.new(function (e) result = e end); local p2 = p:next(function () error(test_error) end); local p3 = p2:next(cb) p3:catch(err_cb); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(0); r("oh doh"); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(1); assert.spy(err_cb).was_called_with("oh doh"); assert.equal("oh doh", result); end); it("propagates values down the chain, even when some handlers are not provided", function () local r; local p = promise.new(function (resolve, reject) r = resolve; end); local cb = spy.new(function () end); local err_cb = spy.new(function () end); local p2 = p:next(function (v) return v; end); local p3 = p2:catch(err_cb) p3:next(cb); assert.spy(cb).was_called(0); assert.spy(err_cb).was_called(0); r(1337); assert.spy(cb).was_called(1); assert.spy(cb).was_called_with(1337); assert.spy(err_cb).was_called(0); end); it("fulfilled promises do not call error handlers and do propagate value", function () local p = promise.resolve("foo"); local cb = spy.new(function () end); local p2 = p:catch(cb); assert.spy(cb).was_called(0); local cb2 = spy.new(function () end); p2:catch(cb2); assert.spy(cb2).was_called(0); end); it("rejected promises do not call fulfilled handlers and do propagate reason", function () local p = promise.reject("foo"); local cb = spy.new(function () end); local p2 = p:next(cb); assert.spy(cb).was_called(0); local cb2 = spy.new(function () end); local cb2_err = spy.new(function () end); p2:next(cb2, cb2_err); assert.spy(cb2).was_called(0); assert.spy(cb2_err).was_called(1); assert.spy(cb2_err).was_called_with("foo"); end); describe("allows callbacks to return", function () it("pending promises", function () local r; local p = promise.resolve() local cb = spy.new(function () return promise.new(function (resolve) r = resolve; end); end); local cb2 = spy.new(function () end); p:next(cb):next(cb2); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(0); r("hello"); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(cb2).was_called_with("hello"); end); it("resolved promises", function () local p = promise.resolve() local cb = spy.new(function () return promise.resolve("hello"); end); local cb2 = spy.new(function () end); p:next(cb):next(cb2); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(cb2).was_called_with("hello"); end); it("rejected promises", function () local p = promise.resolve() local cb = spy.new(function () return promise.reject("hello"); end); local cb2 = spy.new(function () return promise.reject("goodbye"); end); local cb3 = spy.new(function () end); p:next(cb):catch(cb2):catch(cb3); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(cb2).was_called_with("hello"); assert.spy(cb3).was_called(1); assert.spy(cb3).was_called_with("goodbye"); end); it("ordinary values", function () local p = promise.resolve() local cb = spy.new(function () return "hello" end); local cb2 = spy.new(function () end); p:next(cb):next(cb2); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(cb2).was_called_with("hello"); end); it("nil", function () local p = promise.resolve() local cb = spy.new(function () return end); local cb2 = spy.new(function () end); p:next(cb):next(cb2); assert.spy(cb).was_called(1); assert.spy(cb2).was_called(1); assert.spy(cb2).was_called_with(nil); end); end); describe("race()", function () it("works with fulfilled promises", function () local p1, p2 = promise.resolve("yep"), promise.resolve("nope"); local p = promise.race({ p1, p2 }); local result; p:next(function (v) result = v; end); assert.equal("yep", result); end); it("works with pending promises", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.race({ p1, p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); r1("nope"); assert.spy(cb).was_called(1); assert.equal("yep", result); end); end); describe("all()", function () it("works with fulfilled promises", function () local p1, p2 = promise.resolve("yep"), promise.resolve("nope"); local p = promise.all({ p1, p2 }); local result; p:next(function (v) result = v; end); assert.same({ "yep", "nope" }, result); end); it("works with pending promises", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.all({ p1, p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ "nope", "yep" }, result); end); it("rejects if any promise rejects", function () local r1, r2; local p1 = promise.new(function (resolve, reject) r1 = reject end); local p2 = promise.new(function (resolve, reject) r2 = reject end); local p = promise.all({ p1, p2 }); local result; local cb = spy.new(function (v) result = v; end); local cb_err = spy.new(function (v) result = v; end); p:next(cb, cb_err); assert.spy(cb).was_called(0); assert.spy(cb_err).was_called(0); r2("fail"); assert.spy(cb).was_called(0); assert.spy(cb_err).was_called(1); r1("nope"); assert.spy(cb).was_called(0); assert.spy(cb_err).was_called(1); assert.equal("fail", result); end); it("works with non-numeric keys", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.all({ [true] = p1, [false] = p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ [true] = "nope", [false] = "yep" }, result); end); it("passes through non-promise values", function () local r1; local p1 = promise.new(function (resolve) r1 = resolve end); local p = promise.all({ [true] = p1, [false] = "yep" }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ [true] = "nope", [false] = "yep" }, result); end); end); describe("all_settled()", function () it("works with fulfilled promises", function () local p1, p2 = promise.resolve("yep"), promise.resolve("nope"); local p = promise.all_settled({ p1, p2 }); local result; p:next(function (v) result = v; end); assert.same({ { status = "fulfilled", value = "yep" }; { status = "fulfilled", value = "nope" }; }, result); end); it("works with pending promises", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.all_settled({ p1, p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ { status = "fulfilled", value = "nope" }; { status = "fulfilled", value = "yep" }; }, result); end); it("works when some promises reject", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (_, reject) r2 = reject end); local p = promise.all_settled({ p1, p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("this fails"); assert.spy(cb).was_called(0); r1("this succeeds"); assert.spy(cb).was_called(1); assert.same({ { status = "fulfilled", value = "this succeeds" }; { status = "rejected", reason = "this fails" }; }, result); end); it("works with non-numeric keys", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.all_settled({ foo = p1, bar = p2 }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ foo = { status = "fulfilled", value = "nope" }; bar = { status = "fulfilled", value = "yep" }; }, result); end); it("passes through non-promise values", function () local r1; local p1 = promise.new(function (resolve) r1 = resolve end); local p = promise.all_settled({ foo = p1, bar = "yep" }); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same({ foo = { status = "fulfilled", value = "nope" }; bar = "yep"; }, result); end); end); describe("catch()", function () it("works", function () local result; local p = promise.new(function (resolve) error({ foo = true }); end); local cb1 = spy.new(function (v) result = v; end); assert.spy(cb1).was_called(0); p:catch(cb1); assert.spy(cb1).was_called(1); assert.same({ foo = true }, result); end); end); describe("join()", function () it("works", function () local r1, r2; local res1, res2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local p = promise.join(function (_res1, _res2) res1, res2 = _res1, _res2; return promise.resolve("works"); end, p1, p2); local result; local cb = spy.new(function (v) result = v; end); p:next(cb); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(0); r1("nope"); assert.spy(cb).was_called(1); assert.same("works", result); assert.equals("nope", res1); assert.equals("yep", res2); end); end); it("promises may be resolved by other promises", function () local r1, r2; local p1, p2 = promise.new(function (resolve) r1 = resolve end), promise.new(function (resolve) r2 = resolve end); local result; local cb = spy.new(function (v) result = v; end); p1:next(cb); assert.spy(cb).was_called(0); r1(p2); assert.spy(cb).was_called(0); r2("yep"); assert.spy(cb).was_called(1); assert.equal("yep", result); end); describe("reject()", function () it("returns a rejected promise", function () local p = promise.reject("foo"); local cb = spy.new(function () end); p:catch(cb); assert.spy(cb).was_called(1); assert.spy(cb).was_called_with("foo"); end); it("returns a rejected promise and does not call on_fulfilled", function () local p = promise.reject("foo"); local cb = spy.new(function () end); p:next(cb); assert.spy(cb).was_called(0); end); end); describe("finally()", function () local p, p2, resolve, reject, on_finally; before_each(function () p = promise.new(function (_resolve, _reject) resolve, reject = _resolve, _reject; end); on_finally = spy.new(function () end); p2 = p:finally(on_finally); end); it("runs when a promise is resolved", function () assert.spy(on_finally).was_called(0); resolve("foo"); assert.spy(on_finally).was_called(1); assert.spy(on_finally).was_not_called_with("foo"); end); it("runs when a promise is rejected", function () assert.spy(on_finally).was_called(0); reject("foo"); assert.spy(on_finally).was_called(1); assert.spy(on_finally).was_not_called_with("foo"); end); it("returns a promise that fulfills with the original value", function () local cb2 = spy.new(function () end); p2:next(cb2); assert.spy(on_finally).was_called(0); assert.spy(cb2).was_called(0); resolve("foo"); assert.spy(on_finally).was_called(1); assert.spy(cb2).was_called(1); assert.spy(on_finally).was_not_called_with("foo"); assert.spy(cb2).was_called_with("foo"); end); it("returns a promise that rejects with the original error", function () local on_finally_err = spy.new(function () end); local on_finally_ok = spy.new(function () end); p2:catch(on_finally_err); p2:next(on_finally_ok); assert.spy(on_finally).was_called(0); assert.spy(on_finally_err).was_called(0); reject("foo"); assert.spy(on_finally).was_called(1); -- Since the original promise was rejected, the finally promise should also be assert.spy(on_finally_ok).was_called(0); assert.spy(on_finally_err).was_called(1); assert.spy(on_finally).was_not_called_with("foo"); assert.spy(on_finally_err).was_called_with("foo"); end); it("returns a promise that rejects with an uncaught error inside on_finally", function () p = promise.new(function (_resolve, _reject) resolve, reject = _resolve, _reject; end); local test_error = {}; on_finally = spy.new(function () error(test_error) end); p2 = p:finally(on_finally); local on_finally_err = spy.new(function () end); p2:catch(on_finally_err); assert.spy(on_finally).was_called(0); assert.spy(on_finally_err).was_called(0); reject("foo"); assert.spy(on_finally).was_called(1); assert.spy(on_finally_err).was_called(1); assert.spy(on_finally).was_not_called_with("foo"); assert.spy(on_finally).was_not_called_with(test_error); assert.spy(on_finally_err).was_called_with(test_error); end); end); describe("try()", function () it("works with functions that return a promise", function () local resolve; local p = promise.try(function () return promise.new(function (_resolve) resolve = _resolve; end); end); assert.is_function(resolve); local on_resolved = spy.new(function () end); p:next(on_resolved); assert.spy(on_resolved).was_not_called(); resolve("foo"); assert.spy(on_resolved).was_called_with("foo"); end); it("works with functions that return a value", function () local p = promise.try(function () return "foo"; end); local on_resolved = spy.new(function () end); p:next(on_resolved); assert.spy(on_resolved).was_called_with("foo"); end); it("works with functions that return a promise that rejects", function () local reject; local p = promise.try(function () return promise.new(function (_, _reject) reject = _reject; end); end); assert.is_function(reject); local on_rejected = spy.new(function () end); p:catch(on_rejected); assert.spy(on_rejected).was_not_called(); reject("foo"); assert.spy(on_rejected).was_called_with("foo"); end); it("works with functions that throw errors", function () local test_error = {}; local p = promise.try(function () error(test_error); end); local on_rejected = spy.new(function () end); p:catch(on_rejected); assert.spy(on_rejected).was_called(1); assert.spy(on_rejected).was_called_with(test_error); end); end); end);