Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// META: title=IndexedDB: a put() that fails a unique index constraint must be atomic
// META: global=window,worker
// META: script=resources/support.js
'use strict';
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store', {keyPath: 'id'});
store.createIndex('username', 'username', {unique: true});
store.add({id: 1, username: 'foo'});
store.add({id: 2, username: 'bar'});
},
(t, db) => {
// Overwriting id:1 with username 'bar' collides with id:2's unique index
// entry, so the put() must fail with a ConstraintError and leave the
// store exactly as it was.
const tx = db.transaction('store', 'readwrite');
const store = tx.objectStore('store');
const request = store.put({id: 1, username: 'bar'});
request.onsuccess =
t.unreached_func('put() should fail with a ConstraintError');
request.onerror = t.step_func(e => {
assert_equals(request.error.name, 'ConstraintError');
// Handle the error so the transaction commits instead of aborting;
// this is what exposes the non-atomic delete-then-add bug.
e.preventDefault();
});
tx.onabort =
t.unreached_func('transaction should commit, not abort');
tx.oncomplete = t.step_func(() => {
const readTx = db.transaction('store', 'readonly');
const readStore = readTx.objectStore('store');
const getRequest = readStore.get(1);
getRequest.onsuccess = t.step_func(() => {
assert_not_equals(
getRequest.result, undefined,
'the record that would have been overwritten must still exist');
assert_equals(
getRequest.result.username, 'foo',
'the pre-existing record must be unchanged');
});
const countRequest = readStore.count();
countRequest.onsuccess = t.step_func(() => {
assert_equals(
countRequest.result, 2,
'the store must still contain both original records');
});
readTx.oncomplete = t.step_func_done();
});
},
'A put() that fails a unique index constraint must not delete the record ' +
'it would have overwritten');