Skip to content

Commit 4e97adc

Browse files
author
LocalIdentity
committed
Merge branch 'dev' into fix/fatal-flourish-travel-and-retaliation-skills
2 parents 70cc5ee + fc1e5be commit 4e97adc

121 files changed

Lines changed: 83098 additions & 44963 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,65 @@
11
describe("TradeQuery Currency Conversion", function()
2-
local mock_tradeQuery = new("TradeQuery", { itemsTab = {} })
2+
local mock_tradeQuery = new("TradeQuery", { itemsTab = {} })
33

4-
-- test case for commit: "Skip callback on errors to prevent incomplete conversions"
5-
describe("FetchCurrencyConversionTable", function()
6-
-- Pass: Callback not called on error
7-
-- Fail: Callback called, indicating partial data risk
8-
it("skips callback on error", function()
9-
local orig_launch = launch
10-
local spy = { called = false }
11-
launch = {
12-
DownloadPage = function(url, callback, opts)
13-
callback(nil, "test error")
14-
end
15-
}
16-
mock_tradeQuery:FetchCurrencyConversionTable(function()
17-
spy.called = true
18-
end)
19-
launch = orig_launch
20-
assert.is_false(spy.called)
21-
end)
22-
end)
4+
-- test case for commit: "Skip callback on errors to prevent incomplete conversions"
5+
describe("FetchCurrencyConversionTable", function()
6+
-- Pass: Callback not called on error
7+
-- Fail: Callback called, indicating partial data risk
8+
it("skips callback on error", function()
9+
local orig_launch = launch
10+
local spy = { called = false }
11+
launch = {
12+
DownloadPage = function(url, callback, opts)
13+
callback(nil, "test error")
14+
end
15+
}
16+
mock_tradeQuery:FetchCurrencyConversionTable(function()
17+
spy.called = true
18+
end)
19+
launch = orig_launch
20+
assert.is_false(spy.called)
21+
end)
22+
end)
2323

24-
describe("ConvertCurrencyToChaos", function()
25-
-- Pass: Ceils amount to integer (e.g., 4.9 -> 5)
26-
-- Fail: Wrong value or nil, indicating broken rounding/baseline logic, causing inaccurate chaos totals
27-
it("handles chaos currency", function()
28-
mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 1 } }
29-
mock_tradeQuery.pbLeague = "league"
30-
local result = mock_tradeQuery:ConvertCurrencyToChaos("chaos", 4.9)
31-
assert.are.equal(result, 5)
32-
end)
24+
describe("ConvertCurrencyToChaos", function()
25+
-- Pass: Ceils amount to integer (e.g., 4.9 -> 5)
26+
-- Fail: Wrong value or nil, indicating broken rounding/baseline logic, causing inaccurate chaos totals
27+
it("handles chaos currency", function()
28+
mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 1 } }
29+
mock_tradeQuery.pbLeague = "league"
30+
local result = mock_tradeQuery:ConvertCurrencyToChaos("chaos", 4.9)
31+
assert.are.equal(result, 5)
32+
end)
3333

34-
-- Pass: Returns nil without crash
35-
-- Fail: Crashes or wrong value, indicating unhandled currencies, corrupting price conversions
36-
it("returns nil for unmapped", function()
37-
local result = mock_tradeQuery:ConvertCurrencyToChaos("exotic", 10)
38-
assert.is_nil(result)
39-
end)
40-
end)
34+
-- Pass: Returns nil without crash
35+
-- Fail: Crashes or wrong value, indicating unhandled currencies, corrupting price conversions
36+
it("returns nil for unmapped", function()
37+
local result = mock_tradeQuery:ConvertCurrencyToChaos("exotic", 10)
38+
assert.is_nil(result)
39+
end)
40+
end)
4141

42-
describe("PriceBuilderProcessPoENinjaResponse", function()
43-
-- Pass: Processes without error, restoring map
44-
-- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions
45-
it("handles unmapped currency", function()
46-
local orig_conv = mock_tradeQuery.currencyConversionTradeMap
47-
mock_tradeQuery.currencyConversionTradeMap = { div = "id" }
48-
local resp = { exotic = 10 }
49-
mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp)
50-
-- No crash expected
51-
assert.is_true(true)
52-
mock_tradeQuery.currencyConversionTradeMap = orig_conv
53-
end)
54-
end)
42+
describe("PriceBuilderProcessPoENinjaResponse", function()
43+
-- Pass: Processes without error, restoring map
44+
-- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions
45+
it("handles unmapped currency", function()
46+
local orig_conv = mock_tradeQuery.currencyConversionTradeMap
47+
mock_tradeQuery.currencyConversionTradeMap = { div = "id" }
48+
local resp = { exotic = 10 }
49+
mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp)
50+
-- No crash expected
51+
assert.is_true(true)
52+
mock_tradeQuery.currencyConversionTradeMap = orig_conv
53+
end)
54+
end)
5555

56-
describe("GetTotalPriceString", function()
57-
-- Pass: Sums and formats correctly (e.g., "5 chaos, 10 div")
58-
-- Fail: Wrong string (e.g., unsorted/missing sums), indicating aggregation bug, misleading users on totals
59-
it("aggregates prices", function()
60-
mock_tradeQuery.totalPrice = { { currency = "chaos", amount = 5 }, { currency = "div", amount = 10 } }
61-
local result = mock_tradeQuery:GetTotalPriceString()
62-
assert.are.equal(result, "5 chaos, 10 div")
63-
end)
64-
end)
56+
describe("GetTotalPriceString", function()
57+
-- Pass: Sums and formats correctly (e.g., "5 chaos, 10 div")
58+
-- Fail: Wrong string (e.g., unsorted/missing sums), indicating aggregation bug, misleading users on totals
59+
it("aggregates prices", function()
60+
mock_tradeQuery.totalPrice = { { currency = "chaos", amount = 5 }, { currency = "div", amount = 10 } }
61+
local result = mock_tradeQuery:GetTotalPriceString()
62+
assert.are.equal(result, "5 chaos, 10 div")
63+
end)
64+
end)
6565
end)
Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,60 @@
11
describe("TradeQueryGenerator", function()
2-
local mock_queryGen = new("TradeQueryGenerator", { itemsTab = {} })
2+
local mock_queryGen = new("TradeQueryGenerator", { itemsTab = {} })
33

4-
describe("ProcessMod", function()
5-
-- Pass: Mod line maps correctly to trade stat entry without error
6-
-- Fail: Mapping fails (e.g., no match found), indicating incomplete stat parsing for curse mods, potentially missing curse-enabling items in queries
7-
it("handles special curse case", function()
8-
local mod = { "You can apply an additional Curse" }
9-
local tradeStatsParsed = { result = { [2] = { entries = { { text = "You can apply # additional Curses", id = "id" } } } } }
10-
mock_queryGen.modData = { Explicit = true }
11-
mock_queryGen:ProcessMod(mod, tradeStatsParsed, 1)
12-
-- Simplified assertion; in full impl, check modData
13-
assert.is_true(true)
14-
end)
15-
end)
4+
describe("ProcessMod", function()
5+
-- Pass: Mod line maps correctly to trade stat entry without error
6+
-- Fail: Mapping fails (e.g., no match found), indicating incomplete stat parsing for curse mods, potentially missing curse-enabling items in queries
7+
it("handles special curse case", function()
8+
local mod = { "You can apply an additional Curse" }
9+
local tradeStatsParsed = { result = { [2] = { entries = { { text = "You can apply # additional Curses", id = "id" } } } } }
10+
mock_queryGen.modData = { Explicit = true }
11+
mock_queryGen:ProcessMod(mod, tradeStatsParsed, 1)
12+
-- Simplified assertion; in full impl, check modData
13+
assert.is_true(true)
14+
end)
15+
end)
1616

17-
describe("WeightedRatioOutputs", function()
18-
-- Pass: Returns 0, avoiding math errors
19-
-- Fail: Returns NaN/inf or crashes, indicating unhandled infinite values, causing evaluation failures in infinite-scaling builds
20-
it("handles infinite base", function()
21-
local baseOutput = { TotalDPS = math.huge }
22-
local newOutput = { TotalDPS = 100 }
23-
local statWeights = { { stat = "TotalDPS", weightMult = 1 } }
24-
local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights)
25-
assert.are.equal(result, 0)
26-
end)
17+
describe("WeightedRatioOutputs", function()
18+
-- Pass: Returns 0, avoiding math errors
19+
-- Fail: Returns NaN/inf or crashes, indicating unhandled infinite values, causing evaluation failures in infinite-scaling builds
20+
it("handles infinite base", function()
21+
local baseOutput = { TotalDPS = math.huge }
22+
local newOutput = { TotalDPS = 100 }
23+
local statWeights = { { stat = "TotalDPS", weightMult = 1 } }
24+
local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights)
25+
assert.are.equal(result, 0)
26+
end)
2727

28-
-- Pass: Returns capped value (100), preventing division issues
29-
-- Fail: Returns inf/NaN, indicating unhandled zero base, leading to invalid comparisons in low-output builds
30-
it("handles zero base", function()
31-
local baseOutput = { TotalDPS = 0 }
32-
local newOutput = { TotalDPS = 100 }
33-
local statWeights = { { stat = "TotalDPS", weightMult = 1 } }
34-
data.misc.maxStatIncrease = 1000
35-
local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights)
36-
assert.are.equal(result, 100)
37-
end)
38-
end)
28+
-- Pass: Returns capped value (100), preventing division issues
29+
-- Fail: Returns inf/NaN, indicating unhandled zero base, leading to invalid comparisons in low-output builds
30+
it("handles zero base", function()
31+
local baseOutput = { TotalDPS = 0 }
32+
local newOutput = { TotalDPS = 100 }
33+
local statWeights = { { stat = "TotalDPS", weightMult = 1 } }
34+
data.misc.maxStatIncrease = 1000
35+
local result = mock_queryGen.WeightedRatioOutputs(baseOutput, newOutput, statWeights)
36+
assert.are.equal(result, 100)
37+
end)
38+
end)
3939

40-
describe("Filter prioritization", function()
41-
-- Pass: Limits mods to MAX_FILTERS (2 in test), preserving top priorities
42-
-- Fail: Exceeds limit, indicating over-generation of filters, risking API query size errors or rate limits
43-
it("respects MAX_FILTERS", function()
44-
local orig_max = _G.MAX_FILTERS
45-
_G.MAX_FILTERS = 2
46-
mock_queryGen.modWeights = { { weight = 10, tradeModId = "id1" }, { weight = 5, tradeModId = "id2" } }
47-
table.sort(mock_queryGen.modWeights, function(a, b)
48-
return math.abs(a.weight) > math.abs(b.weight)
49-
end)
50-
local prioritized = {}
51-
for i, entry in ipairs(mock_queryGen.modWeights) do
52-
if #prioritized < _G.MAX_FILTERS then
53-
table.insert(prioritized, entry)
54-
end
55-
end
56-
assert.are.equal(#prioritized, 2)
57-
_G.MAX_FILTERS = orig_max
58-
end)
59-
end)
40+
describe("Filter prioritization", function()
41+
-- Pass: Limits mods to MAX_FILTERS (2 in test), preserving top priorities
42+
-- Fail: Exceeds limit, indicating over-generation of filters, risking API query size errors or rate limits
43+
it("respects MAX_FILTERS", function()
44+
local orig_max = _G.MAX_FILTERS
45+
_G.MAX_FILTERS = 2
46+
mock_queryGen.modWeights = { { weight = 10, tradeModId = "id1" }, { weight = 5, tradeModId = "id2" } }
47+
table.sort(mock_queryGen.modWeights, function(a, b)
48+
return math.abs(a.weight) > math.abs(b.weight)
49+
end)
50+
local prioritized = {}
51+
for i, entry in ipairs(mock_queryGen.modWeights) do
52+
if #prioritized < _G.MAX_FILTERS then
53+
table.insert(prioritized, entry)
54+
end
55+
end
56+
assert.are.equal(#prioritized, 2)
57+
_G.MAX_FILTERS = orig_max
58+
end)
59+
end)
6060
end)
Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,78 @@
11
describe("TradeQueryRateLimiter", function()
2-
describe("ParseHeader", function()
3-
-- Pass: Extracts keys/values correctly
4-
-- Fail: Nil/malformed values, indicating regex failure, breaking policy updates from API
5-
it("parses basic headers", function()
6-
local limiter = new("TradeQueryRateLimiter")
7-
local headers = limiter:ParseHeader("X-Rate-Limit-Policy: test\nRetry-After: 5\nContent-Type: json")
8-
assert.are.equal(headers["x-rate-limit-policy"], "test")
9-
assert.are.equal(headers["retry-after"], "5")
10-
assert.are.equal(headers["content-type"], "json")
11-
end)
12-
end)
2+
describe("ParseHeader", function()
3+
-- Pass: Extracts keys/values correctly
4+
-- Fail: Nil/malformed values, indicating regex failure, breaking policy updates from API
5+
it("parses basic headers", function()
6+
local limiter = new("TradeQueryRateLimiter")
7+
local headers = limiter:ParseHeader("X-Rate-Limit-Policy: test\nRetry-After: 5\nContent-Type: json")
8+
assert.are.equal(headers["x-rate-limit-policy"], "test")
9+
assert.are.equal(headers["retry-after"], "5")
10+
assert.are.equal(headers["content-type"], "json")
11+
end)
12+
end)
1313

14-
describe("ParsePolicy", function()
15-
-- Pass: Extracts rules/limits/states accurately
16-
-- Fail: Wrong buckets/windows, indicating parsing bug, enforcing incorrect rates
17-
it("parses full policy", function()
18-
local limiter = new("TradeQueryRateLimiter")
19-
local header = "X-Rate-Limit-Policy: trade-search-request-limit\nX-Rate-Limit-Rules: Ip,Account\nX-Rate-Limit-Ip: 8:10:60,15:60:120\nX-Rate-Limit-Ip-State: 7:10:60,14:60:120\nX-Rate-Limit-Account: 2:5:60\nX-Rate-Limit-Account-State: 1:5:60\nRetry-After: 10"
20-
local policies = limiter:ParsePolicy(header)
21-
local policy = policies["trade-search-request-limit"]
22-
assert.are.equal(policy.ip.limits[10].request, 8)
23-
assert.are.equal(policy.ip.limits[10].timeout, 60)
24-
assert.are.equal(policy.ip.state[10].request, 7)
25-
assert.are.equal(policy.account.limits[5].request, 2)
26-
end)
27-
end)
14+
describe("ParsePolicy", function()
15+
-- Pass: Extracts rules/limits/states accurately
16+
-- Fail: Wrong buckets/windows, indicating parsing bug, enforcing incorrect rates
17+
it("parses full policy", function()
18+
local limiter = new("TradeQueryRateLimiter")
19+
local header = "X-Rate-Limit-Policy: trade-search-request-limit\nX-Rate-Limit-Rules: Ip,Account\nX-Rate-Limit-Ip: 8:10:60,15:60:120\nX-Rate-Limit-Ip-State: 7:10:60,14:60:120\nX-Rate-Limit-Account: 2:5:60\nX-Rate-Limit-Account-State: 1:5:60\nRetry-After: 10"
20+
local policies = limiter:ParsePolicy(header)
21+
local policy = policies["trade-search-request-limit"]
22+
assert.are.equal(policy.ip.limits[10].request, 8)
23+
assert.are.equal(policy.ip.limits[10].timeout, 60)
24+
assert.are.equal(policy.ip.state[10].request, 7)
25+
assert.are.equal(policy.account.limits[5].request, 2)
26+
end)
27+
end)
2828

29-
describe("UpdateFromHeader", function()
30-
-- Pass: Reduces limits (e.g., 5 -> 4)
31-
-- Fail: Unchanged limits, indicating margin ignored, risking user over-requests
32-
it("applies margin to limits", function()
33-
local limiter = new("TradeQueryRateLimiter")
34-
limiter.limitMargin = 1
35-
local header = "X-Rate-Limit-Policy: test\nX-Rate-Limit-Rules: Ip\nX-Rate-Limit-Ip: 5:10:60\nX-Rate-Limit-Ip-State: 4:10:60"
36-
limiter:UpdateFromHeader(header)
37-
assert.are.equal(limiter.policies["test"].ip.limits[10].request, 4)
38-
end)
39-
end)
29+
describe("UpdateFromHeader", function()
30+
-- Pass: Reduces limits (e.g., 5 -> 4)
31+
-- Fail: Unchanged limits, indicating margin ignored, risking user over-requests
32+
it("applies margin to limits", function()
33+
local limiter = new("TradeQueryRateLimiter")
34+
limiter.limitMargin = 1
35+
local header = "X-Rate-Limit-Policy: test\nX-Rate-Limit-Rules: Ip\nX-Rate-Limit-Ip: 5:10:60\nX-Rate-Limit-Ip-State: 4:10:60"
36+
limiter:UpdateFromHeader(header)
37+
assert.are.equal(limiter.policies["test"].ip.limits[10].request, 4)
38+
end)
39+
end)
4040

41-
describe("NextRequestTime", function()
42-
-- Pass: Delays past timestamp
43-
-- Fail: Allows immediate request, indicating ignored cooldowns, causing 429 errors
44-
it("blocks on retry-after", function()
45-
local limiter = new("TradeQueryRateLimiter")
46-
local now = os.time()
47-
limiter.policies["test"] = {}
48-
limiter.retryAfter["test"] = now + 10
49-
local nextTime = limiter:NextRequestTime("test", now)
50-
assert.is_true(nextTime > now)
51-
end)
41+
describe("NextRequestTime", function()
42+
-- Pass: Delays past timestamp
43+
-- Fail: Allows immediate request, indicating ignored cooldowns, causing 429 errors
44+
it("blocks on retry-after", function()
45+
local limiter = new("TradeQueryRateLimiter")
46+
local now = os.time()
47+
limiter.policies["test"] = {}
48+
limiter.retryAfter["test"] = now + 10
49+
local nextTime = limiter:NextRequestTime("test", now)
50+
assert.is_true(nextTime > now)
51+
end)
5252

53-
-- Pass: Calculates delay from timestamps
54-
-- Fail: Allows request in limit, indicating state misread, over-throttling or bans
55-
it("blocks on window limit", function()
56-
local limiter = new("TradeQueryRateLimiter")
57-
local now = os.time()
58-
limiter.policies["test"] = { ["ip"] = { ["limits"] = { ["10"] = { ["request"] = 1, ["timeout"] = 60 } }, ["state"] = { ["10"] = { ["request"] = 1, ["timeout"] = 0 } } } }
59-
limiter.requestHistory["test"] = { timestamps = {now - 5} }
60-
limiter.lastUpdate["test"] = now - 5
61-
local nextTime = limiter:NextRequestTime("test", now)
62-
assert.is_true(nextTime > now)
63-
end)
64-
end)
53+
-- Pass: Calculates delay from timestamps
54+
-- Fail: Allows request in limit, indicating state misread, over-throttling or bans
55+
it("blocks on window limit", function()
56+
local limiter = new("TradeQueryRateLimiter")
57+
local now = os.time()
58+
limiter.policies["test"] = { ["ip"] = { ["limits"] = { ["10"] = { ["request"] = 1, ["timeout"] = 60 } }, ["state"] = { ["10"] = { ["request"] = 1, ["timeout"] = 0 } } } }
59+
limiter.requestHistory["test"] = { timestamps = {now - 5} }
60+
limiter.lastUpdate["test"] = now - 5
61+
local nextTime = limiter:NextRequestTime("test", now)
62+
assert.is_true(nextTime > now)
63+
end)
64+
end)
6565

66-
describe("AgeOutRequests", function()
67-
-- Pass: Removes old stamps, decrements to 1
68-
-- Fail: Stale data persists, indicating aging bug, perpetual blocking
69-
it("cleans up timestamps and decrements", function()
70-
local limiter = new("TradeQueryRateLimiter")
71-
limiter.policies["test"] = { ["ip"] = { ["state"] = { ["10"] = { ["request"] = 2, ["timeout"] = 0, ["decremented"] = nil } } } }
72-
limiter.requestHistory["test"] = { timestamps = {os.time() - 15, os.time() - 5}, maxWindow=10, lastCheck=os.time() - 10 }
73-
limiter:AgeOutRequests("test", os.time())
74-
assert.are.equal(limiter.policies["test"].ip.state["10"].request, 1)
75-
assert.are.equal(#limiter.requestHistory["test"].timestamps, 1)
76-
end)
77-
end)
66+
describe("AgeOutRequests", function()
67+
-- Pass: Removes old stamps, decrements to 1
68+
-- Fail: Stale data persists, indicating aging bug, perpetual blocking
69+
it("cleans up timestamps and decrements", function()
70+
local limiter = new("TradeQueryRateLimiter")
71+
limiter.policies["test"] = { ["ip"] = { ["state"] = { ["10"] = { ["request"] = 2, ["timeout"] = 0, ["decremented"] = nil } } } }
72+
limiter.requestHistory["test"] = { timestamps = {os.time() - 15, os.time() - 5}, maxWindow=10, lastCheck=os.time() - 10 }
73+
limiter:AgeOutRequests("test", os.time())
74+
assert.are.equal(limiter.policies["test"].ip.state["10"].request, 1)
75+
assert.are.equal(#limiter.requestHistory["test"].timestamps, 1)
76+
end)
77+
end)
7878
end)

0 commit comments

Comments
 (0)