Merge lp:~charlesk/pay-service/fix-status-lookups into lp:pay-service/15.04
- fix-status-lookups
- Merge into trunk.15.04
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 160 |
Merged at revision: | 52 |
Proposed branch: | lp:~charlesk/pay-service/fix-status-lookups |
Merge into: | lp:pay-service/15.04 |
Prerequisite: | lp:~dobey/pay-service/more-cleanups |
Diff against target: |
327 lines (+81/-78) 5 files modified
libpay/internal/item.h (+3/-0) libpay/internal/package.cpp (+14/-61) libpay/internal/package.h (+0/-6) tests/com_canonical_pay_store.py (+2/-1) tests/libpay-package-tests.cpp (+62/-10) |
To merge this branch: | bzr merge lp:~charlesk/pay-service/fix-status-lookups |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
dobey (community) | Approve | ||
Review via email: mp+273496@code.launchpad.net |
Commit message
Fix the libpay cold cache bug.
Description of the change
Fix the libpay cold cache bug.
This has the same intent as lp:~dobey/pay-service/fix-refunds but uses a different approach.
* itemStatus() has a sibling bug; fix it too
* implement both itemStatus() and refundStatus() as calls to getItem(). Since getItem() is already blocking, the new status functions have trivial implementation
* adds 'cold cache' tests
* remove unused itemStatusCache
- 150. By Charles Kerr
-
remove unused copypasta from new tests
PS Jenkins bot (ps-jenkins) wrote : | # |
- 151. By Charles Kerr
-
allow refundable_until as an app property in the store mock's AddStore()
- 152. By Charles Kerr
-
in libpay package tests' setup, populate the mock service with apps
- 153. By Charles Kerr
-
add 'cache miss' test for libpay packages' item and refund statuses
- 154. By Charles Kerr
-
remove unused variables from LibpayPackageTest tests
- 155. By Charles Kerr
-
fix typo in dbusmock's refundable_until test
- 156. By Charles Kerr
-
add item status & refund status checks to LibpayPackageTe
sts::PurchaseIt em - 157. By Charles Kerr
-
re-fix the store mock's refund() test for refundable_until... r155 fixed the typo not the property name
- 158. By Charles Kerr
-
add item status & refund status checks to LibpayPackageTe
sts::RefundItem - 159. By Charles Kerr
-
copyediting: fix comments
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:158
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 160. By Charles Kerr
-
remove 'approved_app' test -- app purchases don't have an 'approved' status
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:159
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
dobey (dobey) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:160
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'libpay/internal/item.h' |
2 | --- libpay/internal/item.h 2015-10-06 17:18:08 +0000 |
3 | +++ libpay/internal/item.h 2015-10-06 17:18:08 +0000 |
4 | @@ -40,6 +40,7 @@ |
5 | uint64_t m_purchase_id = 0; |
6 | time_t m_completed_timestamp = 0; |
7 | time_t m_acknowledged_timestamp = 0; |
8 | + time_t m_refundable_until = 0; |
9 | |
10 | public: |
11 | |
12 | @@ -63,6 +64,7 @@ |
13 | PayPackageItemStatus status() const {return m_status;} |
14 | time_t completed_timestamp() const {return m_completed_timestamp;} |
15 | time_t acknowledged_timestamp() const {return m_acknowledged_timestamp;} |
16 | + time_t refundable_until() const {return m_refundable_until;} |
17 | const std::string& title() const {return m_title;} |
18 | PayItemType type() const {return m_type;} |
19 | |
20 | @@ -74,6 +76,7 @@ |
21 | void set_status(PayPackageItemStatus val) {m_status = val;} |
22 | void set_completed_timestamp(time_t val) {m_completed_timestamp = val;} |
23 | void set_acknowledged_timestamp(time_t val) {m_acknowledged_timestamp = val;} |
24 | + void set_refundable_until(time_t val) {m_refundable_until = val;} |
25 | void set_title(const std::string& val) {m_title = val;} |
26 | void set_type(PayItemType val) {m_type = val;} |
27 | |
28 | |
29 | === modified file 'libpay/internal/package.cpp' |
30 | --- libpay/internal/package.cpp 2015-10-06 17:18:08 +0000 |
31 | +++ libpay/internal/package.cpp 2015-10-06 17:18:08 +0000 |
32 | @@ -34,16 +34,6 @@ |
33 | : id(packageid) |
34 | , thread([]{}, [this]{storeProxy.reset();}) |
35 | { |
36 | - // when item statuses change, update our internal cache |
37 | - statusChanged.connect([this](const std::string& sku, |
38 | - PayPackageItemStatus status, |
39 | - uint64_t refundable_until) |
40 | - { |
41 | - g_debug("Updating itemStatusCache for '%s', timeout is: %lld", |
42 | - sku.c_str(), refundable_until); |
43 | - itemStatusCache[sku] = std::make_pair(status, refundable_until); |
44 | - }); |
45 | - |
46 | /* Fire up a glib thread to create the proxies. |
47 | Block on it here so the proxies are ready before this ctor returns */ |
48 | const auto errorStr = thread.executeOnThread<std::string>([this]() |
49 | @@ -90,28 +80,21 @@ |
50 | PayPackageItemStatus |
51 | Package::itemStatus (const std::string& sku) noexcept |
52 | { |
53 | - try |
54 | - { |
55 | - return itemStatusCache[sku].first; |
56 | - } |
57 | - catch (std::out_of_range& /*range*/) |
58 | - { |
59 | - return PAY_PACKAGE_ITEM_STATUS_UNKNOWN; |
60 | - } |
61 | + const auto item = getItem(sku); |
62 | + |
63 | + return item |
64 | + ? item->status() |
65 | + : PAY_PACKAGE_ITEM_STATUS_UNKNOWN; |
66 | } |
67 | |
68 | PayPackageRefundStatus |
69 | Package::refundStatus (const std::string& sku) noexcept |
70 | { |
71 | - try |
72 | - { |
73 | - auto entry = itemStatusCache[sku]; |
74 | - return calcRefundStatus(entry.first, entry.second); |
75 | - } |
76 | - catch (std::out_of_range& /*range*/) |
77 | - { |
78 | - return PAY_PACKAGE_REFUND_STATUS_NOT_REFUNDABLE; |
79 | - } |
80 | + const auto item = getItem(sku); |
81 | + |
82 | + return item |
83 | + ? calcRefundStatus(item->status(), item->refundable_until()) |
84 | + : PAY_PACKAGE_REFUND_STATUS_NOT_REFUNDABLE; |
85 | } |
86 | |
87 | PayPackageRefundStatus |
88 | @@ -263,6 +246,10 @@ |
89 | { |
90 | item->set_completed_timestamp(g_variant_get_uint64(value)); |
91 | } |
92 | + else if (!g_strcmp0(key, "refundable_until")) |
93 | + { |
94 | + item->set_refundable_until(g_variant_get_uint64(value)); |
95 | + } |
96 | else if (!g_strcmp0(key, "purchase_id")) |
97 | { |
98 | item->set_purchase_id(g_variant_get_uint64(value)); |
99 | @@ -546,40 +533,6 @@ |
100 | return ok; |
101 | } |
102 | |
103 | -template <typename BusProxy, |
104 | - void (*startFunc)(BusProxy*, const gchar*, GCancellable*, GAsyncReadyCallback, gpointer), |
105 | - gboolean (*finishFunc)(BusProxy*, GAsyncResult*, GError**)> |
106 | -bool Package::startBase (const std::shared_ptr<BusProxy>& bus_proxy, const std::string& sku) noexcept |
107 | -{ |
108 | - auto async_ready = [](GObject * obj, GAsyncResult * res, gpointer user_data) -> void |
109 | - { |
110 | - auto prom = static_cast<std::promise<bool>*>(user_data); |
111 | - GError* error = nullptr; |
112 | - finishFunc(reinterpret_cast<BusProxy*>(obj), res, &error); |
113 | - if ((error != nullptr) && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
114 | - { |
115 | - std::cerr << "Error from service: " << error->message << std::endl; |
116 | - } |
117 | - prom->set_value(error == nullptr); |
118 | - g_clear_error(&error); |
119 | - }; |
120 | - |
121 | - std::promise<bool> promise; |
122 | - thread.executeOnThread([this, bus_proxy, sku, &async_ready, &promise]() |
123 | - { |
124 | - startFunc(bus_proxy.get(), |
125 | - sku.c_str(), |
126 | - thread.getCancellable().get(), // GCancellable |
127 | - async_ready, |
128 | - &promise); |
129 | - }); |
130 | - |
131 | - auto future = promise.get_future(); |
132 | - future.wait(); |
133 | - auto call_succeeded = future.get(); |
134 | - return call_succeeded; |
135 | -} |
136 | - |
137 | } // namespace Internal |
138 | |
139 | } // namespace Pay |
140 | |
141 | === modified file 'libpay/internal/package.h' |
142 | --- libpay/internal/package.h 2015-10-06 17:18:08 +0000 |
143 | +++ libpay/internal/package.h 2015-10-06 17:18:08 +0000 |
144 | @@ -50,7 +50,6 @@ |
145 | std::map <std::pair<PayPackageRefundObserver, void*>, core::ScopedConnection> refundObservers; |
146 | |
147 | core::Signal<std::string, PayPackageItemStatus, uint64_t> statusChanged; |
148 | - std::map <std::string, std::pair<PayPackageItemStatus, uint64_t>> itemStatusCache; |
149 | void updateStatus(const std::string& sku, PayPackageItemStatus); |
150 | |
151 | GLib::ContextThread thread; |
152 | @@ -61,11 +60,6 @@ |
153 | template<typename Collection> |
154 | bool removeObserver(Collection& collection, const typename Collection::key_type& key); |
155 | |
156 | - template <typename BusProxy, |
157 | - void (*startFunc)(BusProxy*, const gchar*, GCancellable*, GAsyncReadyCallback, gpointer), |
158 | - gboolean (*finishFunc)(BusProxy*, GAsyncResult*, GError**)> |
159 | - bool startBase (const std::shared_ptr<BusProxy>& bus_proxy, const std::string& sku) noexcept; |
160 | - |
161 | template<typename BusProxy, |
162 | gboolean (*finish_func)(BusProxy*, GVariant**, GAsyncResult*, GError**)> |
163 | bool startStoreAction(const std::shared_ptr<BusProxy>& bus_proxy, |
164 | |
165 | === modified file 'tests/com_canonical_pay_store.py' |
166 | --- tests/com_canonical_pay_store.py 2015-10-06 17:18:08 +0000 |
167 | +++ tests/com_canonical_pay_store.py 2015-10-06 17:18:08 +0000 |
168 | @@ -62,6 +62,7 @@ |
169 | 'description': dbus.String('The is a default item'), |
170 | 'price': dbus.String('$1'), |
171 | 'purchase_id': dbus.UInt64(0.0), |
172 | + 'refundable_until': dbus.UInt64(0.0), |
173 | 'sku': dbus.String('default_item'), |
174 | 'state': dbus.String('available'), |
175 | 'type': dbus.String('unlockable'), |
176 | @@ -170,7 +171,7 @@ |
177 | try: |
178 | item = store.items[sku] |
179 | if (item.bus_properties['state'] == 'purchased' and |
180 | - item.bus_properties['refund_tiemout'] > dbus.UInt64(time.time())): |
181 | + item.bus_properties['refundable_until'] > dbus.UInt64(time.time())): |
182 | del store.items[sku] |
183 | return dbus.Dictionary({ |
184 | 'state': 'available', |
185 | |
186 | === modified file 'tests/libpay-package-tests.cpp' |
187 | --- tests/libpay-package-tests.cpp 2015-10-06 17:18:08 +0000 |
188 | +++ tests/libpay-package-tests.cpp 2015-10-06 17:18:08 +0000 |
189 | @@ -69,8 +69,33 @@ |
190 | |
191 | wait_for_store_service(); |
192 | |
193 | + const guint64 now = time(nullptr); |
194 | + const struct { |
195 | + const char * sku; |
196 | + const char * state; |
197 | + guint64 refundable_until; |
198 | + } prefab_apps[] = { |
199 | + { "available_app", "available", 0 }, // not purchased |
200 | + { "newly_purchased_app", "purchased", now+(60*14) }, // purchased, refund window open |
201 | + { "old_purchased_app", "purchased", now-(60*30) } // purchased, refund window closed |
202 | + }; |
203 | GVariantBuilder b; |
204 | g_variant_builder_init(&b, G_VARIANT_TYPE("aa{sv}")); |
205 | + for (const auto& app : prefab_apps) { |
206 | + GVariantBuilder app_props; |
207 | + g_variant_builder_init(&app_props, G_VARIANT_TYPE_VARDICT); |
208 | + const struct { |
209 | + const char* key; |
210 | + GVariant* value; |
211 | + } entries[] = { |
212 | + { "sku", g_variant_new_string(app.sku) }, |
213 | + { "state", g_variant_new_string(app.state) }, |
214 | + { "refundable_until", g_variant_new_uint64(app.refundable_until) } |
215 | + }; |
216 | + for (const auto& entry : entries) |
217 | + g_variant_builder_add(&app_props, "{sv}", entry.key, entry.value); |
218 | + g_variant_builder_add_value(&b, g_variant_builder_end(&app_props)); |
219 | + } |
220 | auto props = g_variant_builder_end(&b); |
221 | |
222 | GVariant* args[] = { g_variant_new_string("click-scope"), props }; |
223 | @@ -135,15 +160,17 @@ |
224 | |
225 | TEST_F(LibpayPackageTests, PurchaseItem) |
226 | { |
227 | - GError* error = nullptr; |
228 | - guint callcount = 0; |
229 | auto package = pay_package_new("click-scope"); |
230 | + const char* sku {"available_app"}; |
231 | + |
232 | + // pre-purchase tests |
233 | + EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED, pay_package_item_status(package, sku)); |
234 | + EXPECT_EQ(PAY_PACKAGE_REFUND_STATUS_NOT_PURCHASED, pay_package_refund_status(package, sku)); |
235 | |
236 | // install a status observer |
237 | StatusObserverData data; |
238 | InstallStatusObserver(package, data); |
239 | |
240 | - const char* sku = "item"; |
241 | EXPECT_TRUE(pay_package_item_start_purchase(package, sku)); |
242 | |
243 | // wait for the call to complete |
244 | @@ -156,6 +183,7 @@ |
245 | EXPECT_EQ(package, data.package); |
246 | EXPECT_EQ(sku, data.sku); |
247 | EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_PURCHASED, data.status); |
248 | + EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_PURCHASED, pay_package_item_status(package, sku)); |
249 | |
250 | // cleanup |
251 | pay_package_delete(package); |
252 | @@ -163,8 +191,6 @@ |
253 | |
254 | TEST_F(LibpayPackageTests, PurchaseItemCancelled) |
255 | { |
256 | - GError* error = nullptr; |
257 | - guint callcount = 0; |
258 | auto package = pay_package_new("click-scope"); |
259 | |
260 | // install a status observer |
261 | @@ -191,15 +217,17 @@ |
262 | |
263 | TEST_F(LibpayPackageTests, RefundItem) |
264 | { |
265 | - GError* error = nullptr; |
266 | - guint callcount = 0; |
267 | auto package = pay_package_new("click-scope"); |
268 | + const char* sku {"newly_purchased_app"}; |
269 | + |
270 | + // pre-refund tests |
271 | + EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_PURCHASED, pay_package_item_status(package, sku)); |
272 | + EXPECT_EQ(PAY_PACKAGE_REFUND_STATUS_REFUNDABLE, pay_package_refund_status(package, sku)); |
273 | |
274 | // install a status observer |
275 | StatusObserverData data; |
276 | InstallStatusObserver(package, data); |
277 | |
278 | - const char* sku = "item"; |
279 | EXPECT_TRUE(pay_package_item_start_refund(package, sku)); |
280 | |
281 | // wait for the call to complete |
282 | @@ -212,6 +240,8 @@ |
283 | EXPECT_EQ(package, data.package); |
284 | EXPECT_EQ(sku, data.sku); |
285 | EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED, data.status); |
286 | + EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED, pay_package_item_status(package, sku)); |
287 | + EXPECT_EQ(PAY_PACKAGE_REFUND_STATUS_NOT_PURCHASED, pay_package_refund_status(package, sku)); |
288 | |
289 | // cleanup |
290 | pay_package_delete(package); |
291 | @@ -219,8 +249,6 @@ |
292 | |
293 | TEST_F(LibpayPackageTests, VerifyItem) |
294 | { |
295 | - GError* error = nullptr; |
296 | - guint callcount = 0; |
297 | auto package = pay_package_new("click-scope"); |
298 | |
299 | // install a status observer |
300 | @@ -244,3 +272,27 @@ |
301 | // cleanup |
302 | pay_package_delete(package); |
303 | } |
304 | + |
305 | +TEST_F(LibpayPackageTests, ColdCacheStatus) |
306 | +{ |
307 | + auto package = pay_package_new("click-scope"); |
308 | + |
309 | + const struct { |
310 | + const char * sku; |
311 | + PayPackageItemStatus expected_item_status; |
312 | + PayPackageRefundStatus expected_refund_status; |
313 | + } tests[] = { |
314 | + { "available_app", PAY_PACKAGE_ITEM_STATUS_NOT_PURCHASED, PAY_PACKAGE_REFUND_STATUS_NOT_PURCHASED }, |
315 | + { "newly_purchased_app", PAY_PACKAGE_ITEM_STATUS_PURCHASED, PAY_PACKAGE_REFUND_STATUS_REFUNDABLE }, |
316 | + { "old_purchased_app", PAY_PACKAGE_ITEM_STATUS_PURCHASED, PAY_PACKAGE_REFUND_STATUS_NOT_REFUNDABLE } |
317 | + }; |
318 | + |
319 | + for (const auto& test : tests) { |
320 | + EXPECT_EQ(test.expected_item_status, pay_package_item_status(package, test.sku)); |
321 | + EXPECT_EQ(test.expected_refund_status, pay_package_refund_status(package, test.sku)); |
322 | + } |
323 | + |
324 | + // cleanup |
325 | + pay_package_delete(package); |
326 | +} |
327 | + |
PASSED: Continuous integration, rev:150 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-ci/ 56/ jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- amd64-ci/ 56 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- armhf-ci/ 56 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- armhf-ci/ 56/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/pay- service- 15.04-ci/ 56/rebuild
http://