Merge lp:~charlesk/pay-service/fix-status-lookups into lp:pay-service/15.04

Proposed by Charles Kerr
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
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

To post a comment you must log in.
150. By Charles Kerr

remove unused copypasta from new tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
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 LibpayPackageTests::PurchaseItem

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 LibpayPackageTests::RefundItem

159. By Charles Kerr

copyediting: fix comments

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
160. By Charles Kerr

remove 'approved_app' test -- app purchases don't have an 'approved' status

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
dobey (dobey) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+

Subscribers

People subscribed via source and target branches

to all changes: