blob: a207a8fae620c5fe1f579dff743213ee7466149d [file] [log] [blame]
Marc Kupietz37f96072026-09-03 07:18:11 +02001offlineConnection <- function() {
2 methods::new(
3 "KorAPConnection",
4 apiUrl = "https://example.invalid/",
5 KorAPUrl = "https://example.invalid/",
6 # keeps the unrelated "authorize your application" warning out of the way
7 authorizationSupported = FALSE,
8 verbose = FALSE
9 )
10}
11
12# Returning no candidates lets collocationAnalysis() finish without contacting
13# the server, so that the caching around it can be tested offline.
14mockEmptyAnalysis <- function() {
15 testthat::local_mocked_bindings(
16 collocatesQuery = function(...) tibble::tibble(),
17 .package = "RKorAPClient",
18 .env = parent.frame()
19 )
20}
21
22test_that("cache files record the parameters of the analysis", {
23 mockEmptyAnalysis()
24 cacheFile <- tempfile(fileext = ".rds")
25 on.exit(unlink(cacheFile), add = TRUE)
26
27 collocationAnalysis(offlineConnection(), "Test", minOccur = 3, cacheAs = cacheFile)
28
Marc Kupietzc902c232026-09-08 07:58:01 +020029 stored <- attr(readRDS(cacheFile), RKorAPClient:::cacheAsAttribute)
Marc Kupietz37f96072026-09-03 07:18:11 +020030 expect_false(is.null(stored))
Marc Kupietzc902c232026-09-08 07:58:01 +020031 expect_equal(stored$scoreVersion, RKorAPClient:::cacheAsScoreVersion)
32 expect_equal(stored$packageVersion, as.character(utils::packageVersion("RKorAPClient")))
Marc Kupietz37f96072026-09-03 07:18:11 +020033 expect_equal(stored$parameters$node, "Test")
34 expect_equal(stored$parameters$minOccur, 3)
35 expect_equal(stored$apiUrl, "https://example.invalid/")
36 # every parameter of the analysis is recorded, not just those visible in the
37 # result, so that added parameters are covered automatically
38 expect_true(all(c(
39 "topCollocatesLimit", "searchHitsSampleLimit", "exactFrequencies",
40 "stopwords", "threshold", "collocateFilterRegex", "seed"
41 ) %in% names(stored$parameters)))
42 # kco and cacheAs are not parameters of the analysis
43 expect_false(any(c("kco", "cacheAs") %in% names(stored$parameters)))
44})
45
46test_that("the returned result is the same whether it was cached or not", {
47 mockEmptyAnalysis()
48 cacheFile <- tempfile(fileext = ".rds")
49 on.exit(unlink(cacheFile), add = TRUE)
50 kco <- offlineConnection()
51
52 fresh <- collocationAnalysis(kco, "Test", cacheAs = cacheFile)
53 fromCache <- collocationAnalysis(kco, "Test", cacheAs = cacheFile)
54
55 expect_equal(fromCache, fresh)
56 # the parameters live in the file only, not in the returned value
Marc Kupietzc902c232026-09-08 07:58:01 +020057 expect_null(attr(fromCache, RKorAPClient:::cacheAsAttribute))
Marc Kupietz37f96072026-09-03 07:18:11 +020058})
59
60test_that("an unchanged call is served from the cache without contacting the server", {
61 cacheFile <- tempfile(fileext = ".rds")
62 on.exit(unlink(cacheFile), add = TRUE)
63 kco <- offlineConnection()
64
65 local({
66 mockEmptyAnalysis()
67 collocationAnalysis(kco, "Test", minOccur = 3, cacheAs = cacheFile)
68 })
69
70 testthat::local_mocked_bindings(
71 collocatesQuery = function(...) stop("server must not be contacted"),
72 .package = "RKorAPClient"
73 )
74 expect_no_warning(collocationAnalysis(kco, "Test", minOccur = 3, cacheAs = cacheFile))
75})
76
77test_that("changed parameters make the analysis be recomputed, with a warning", {
78 cacheFile <- tempfile(fileext = ".rds")
79 on.exit(unlink(cacheFile), add = TRUE)
80 kco <- offlineConnection()
81
82 local({
83 mockEmptyAnalysis()
84 collocationAnalysis(kco, "Test", minOccur = 3, cacheAs = cacheFile)
85 })
86
87 testthat::local_mocked_bindings(
88 collocatesQuery = function(...) stop("recomputed"),
89 .package = "RKorAPClient"
90 )
91
92 # the warning names the parameter that differs, and the analysis is rerun
93 expect_warning(
94 expect_error(
95 collocationAnalysis(kco, "Test", minOccur = 5, cacheAs = cacheFile),
96 "recomputed"
97 ),
98 "minOccur"
99 )
100
101 # a changed node is caught as well
102 expect_warning(
103 expect_error(
104 collocationAnalysis(kco, "Other", minOccur = 3, cacheAs = cacheFile),
105 "recomputed"
106 ),
107 "node"
108 )
109})
110
111test_that("a recomputed analysis overwrites the stale cache file", {
112 cacheFile <- tempfile(fileext = ".rds")
113 on.exit(unlink(cacheFile), add = TRUE)
114 kco <- offlineConnection()
115
116 local({
117 mockEmptyAnalysis()
118 collocationAnalysis(kco, "Test", minOccur = 3, cacheAs = cacheFile)
119 })
120
121 local({
122 mockEmptyAnalysis()
123 expect_warning(
124 collocationAnalysis(kco, "Test", minOccur = 5, cacheAs = cacheFile),
125 "minOccur"
126 )
127 })
128
Marc Kupietzc902c232026-09-08 07:58:01 +0200129 stored <- attr(readRDS(cacheFile), RKorAPClient:::cacheAsAttribute)
Marc Kupietz37f96072026-09-03 07:18:11 +0200130 expect_equal(stored$parameters$minOccur, 5)
131})
132
Marc Kupietzc902c232026-09-08 07:58:01 +0200133test_that("cache files written before the scores were corrected are refused", {
134 mockEmptyAnalysis()
Marc Kupietz37f96072026-09-03 07:18:11 +0200135 cacheFile <- tempfile(fileext = ".rds")
136 on.exit(unlink(cacheFile), add = TRUE)
137 kco <- offlineConnection()
138
Marc Kupietzc902c232026-09-08 07:58:01 +0200139 # as written by RKorAPClient 1.3.0, whose logDice and ll differ from today's
Marc Kupietz37f96072026-09-03 07:18:11 +0200140 legacy <- tibble::tibble(node = "Test", collocate = "c", logDice = 7)
141 saveRDS(legacy, cacheFile)
142
Marc Kupietzc902c232026-09-08 07:58:01 +0200143 expect_warning(
144 result <- collocationAnalysis(kco, "Test", cacheAs = cacheFile),
145 "logDice"
Marc Kupietz37f96072026-09-03 07:18:11 +0200146 )
Marc Kupietzc902c232026-09-08 07:58:01 +0200147 expect_false(identical(result, legacy))
148 # and the stale file is replaced by one that records what wrote it
149 expect_false(is.null(attr(readRDS(cacheFile), RKorAPClient:::cacheAsAttribute)))
Marc Kupietz37f96072026-09-03 07:18:11 +0200150})
151
Marc Kupietzc902c232026-09-08 07:58:01 +0200152test_that("cache files of an older version are refused, naming it", {
153 mockEmptyAnalysis()
154 cacheFile <- tempfile(fileext = ".rds")
155 on.exit(unlink(cacheFile), add = TRUE)
156 kco <- offlineConnection()
157
158 collocationAnalysis(kco, "Test", cacheAs = cacheFile)
159 aged <- readRDS(cacheFile)
160 record <- attr(aged, RKorAPClient:::cacheAsAttribute)
161 record$scoreVersion <- "1.3.0"
162 record$packageVersion <- "1.3.0"
163 attr(aged, RKorAPClient:::cacheAsAttribute) <- record
164 saveRDS(aged, cacheFile)
165
166 expect_warning(collocationAnalysis(kco, "Test", cacheAs = cacheFile), "1\\.3\\.0")
167})
168
169test_that("cacheAsRejectionReason says why a cache file cannot be used", {
170 reason <- RKorAPClient:::cacheAsRejectionReason
Marc Kupietz37f96072026-09-03 07:18:11 +0200171
172 stored <- list(
Marc Kupietzc902c232026-09-08 07:58:01 +0200173 scoreVersion = RKorAPClient:::cacheAsScoreVersion,
Marc Kupietz37f96072026-09-03 07:18:11 +0200174 parameters = list(node = "Test", minOccur = 3, vc = ""),
175 dots = list(),
176 apiUrl = "https://korap.ids-mannheim.de/api/v1.0/"
177 )
178
Marc Kupietzc902c232026-09-08 07:58:01 +0200179 expect_null(reason(stored, stored))
180 expect_match(reason(NULL, stored), "before RKorAPClient")
181
182 # a file from before the corrections: an older score generation, and the
183 # version that wrote it, which the reason names
184 aged <- stored
185 aged$scoreVersion <- "1.3.0"
186 aged$packageVersion <- "1.3.0"
187 expect_match(reason(aged, stored), "written by RKorAPClient 1\\.3\\.0")
188
189 # one that does not even say what wrote it
190 anonymous <- stored
191 anonymous$scoreVersion <- NULL
192 expect_match(reason(anonymous, stored), "written before RKorAPClient")
Marc Kupietz37f96072026-09-03 07:18:11 +0200193
194 changed <- stored
195 changed$parameters$minOccur <- 5
Marc Kupietzc902c232026-09-08 07:58:01 +0200196 expect_match(reason(stored, changed), "minOccur")
Marc Kupietz37f96072026-09-03 07:18:11 +0200197
Marc Kupietz37f96072026-09-03 07:18:11 +0200198 changed$parameters$vc <- "textType=/Zeit.*/"
Marc Kupietzc902c232026-09-08 07:58:01 +0200199 expect_match(reason(stored, changed), "minOccur, vc")
Marc Kupietz37f96072026-09-03 07:18:11 +0200200
201 changed <- stored
202 changed$dots <- list(smoothingConstant = 1)
Marc Kupietzc902c232026-09-08 07:58:01 +0200203 expect_match(reason(stored, changed), "\\.\\.\\.")
Marc Kupietz37f96072026-09-03 07:18:11 +0200204
205 changed <- stored
206 changed$apiUrl <- "https://korap.dnb.de/api/v1.0/"
Marc Kupietzc902c232026-09-08 07:58:01 +0200207 expect_match(reason(stored, changed), "KorAP instance")
Marc Kupietz37f96072026-09-03 07:18:11 +0200208})