summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/IndexedDB/key-conversion-exceptions.htm
blob: 9fdab58eb1212f911203177aac04d57440245d94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<!doctype html>
<meta charset=utf-8>
<title>IndexedDB: Exceptions thrown during key conversion</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/support.js"></script>
<script>

// Convenience function for tests that only need to run code in onupgradeneeded.
function indexeddb_upgrade_only_test(upgrade_callback, description) {
  indexeddb_test(upgrade_callback, t => { t.done(); }, description);
}

// Key that throws during conversion.
function throwing_key(name) {
    var throws = [];
    throws.length = 1;
    const err = new Error('throwing from getter');
    err.name = name;
    Object.defineProperty(throws, '0', {get: function() {
        throw err;
    }, enumerable: true});
    return [throws, err];
}

var valid_key = [];
var invalid_key = {};

// Calls method on receiver with the specified number of args (default 1)
// and asserts that the method fails appropriately (rethrowing if
// conversion throws, or DataError if not a valid key), and that
// the first argument is fully processed before the second argument
// (if appropriate).
function check_method(receiver, method, args) {
    args = args || 1;
    if (args < 2) {
        const [key, err] = throwing_key('getter');
        assert_throws_exactly(err, () => {
            receiver[method](key);
        }, 'key conversion with throwing getter should rethrow');

        assert_throws_dom('DataError', () => {
            receiver[method](invalid_key);
        }, 'key conversion with invalid key should throw DataError');
    } else {
        const [key1, err1] = throwing_key('getter 1');
        const [key2, err2] = throwing_key('getter 2');
        assert_throws_exactly(err1, () => {
            receiver[method](key1, key2);
        }, 'first key conversion with throwing getter should rethrow');

        assert_throws_dom('DataError', () => {
            receiver[method](invalid_key, key2);
        }, 'first key conversion with invalid key should throw DataError');

        assert_throws_exactly(err2, () => {
            receiver[method](valid_key, key2);
        }, 'second key conversion with throwing getter should rethrow');

        assert_throws_dom('DataError', () => {
            receiver[method](valid_key, invalid_key);
        }, 'second key conversion with invalid key should throw DataError');
    }
}

// Static key comparison utility on IDBFactory.
test(t => {
    check_method(indexedDB, 'cmp', 2);
}, 'IDBFactory cmp() static with throwing/invalid keys');

// Continue methods on IDBCursor.
indexeddb_upgrade_only_test((t, db) => {
    var store = db.createObjectStore('store');
    store.put('a', 1).onerror = t.unreached_func('put should succeed');

    var request = store.openCursor();
    request.onerror = t.unreached_func('openCursor should succeed');
    request.onsuccess = t.step_func(() => {
        var cursor = request.result;
        assert_not_equals(cursor, null, 'cursor should find a value');
        check_method(cursor, 'continue');
    });
}, 'IDBCursor continue() method with throwing/invalid keys');

indexeddb_upgrade_only_test((t, db) => {
    var store = db.createObjectStore('store');
    var index = store.createIndex('index', 'prop');
    store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed');

    var request = index.openCursor();
    request.onerror = t.unreached_func('openCursor should succeed');
    request.onsuccess = t.step_func(() => {
        var cursor = request.result;
        assert_not_equals(cursor, null, 'cursor should find a value');

        check_method(cursor, 'continuePrimaryKey', 2);
    });
}, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys');

// Mutation methods on IDBCursor.
indexeddb_upgrade_only_test((t, db) => {
    var store = db.createObjectStore('store', {keyPath: 'prop'});
    store.put({prop: 1}).onerror = t.unreached_func('put should succeed');

    var request = store.openCursor();
    request.onerror = t.unreached_func('openCursor should succeed');
    request.onsuccess = t.step_func(() => {
        var cursor = request.result;
        assert_not_equals(cursor, null, 'cursor should find a value');

        var value = {};
        var err;
        [value.prop, err] = throwing_key('getter');
        assert_throws_exactly(err, () => {
            cursor.update(value);
        }, 'throwing getter should rethrow during clone');

        // Throwing from the getter during key conversion is
        // not possible since (1) a clone is used, (2) only own
        // properties are cloned, and (3) only own properties
        // are used for key path evaluation.

        value.prop = invalid_key;
        assert_throws_dom('DataError', () => {
            cursor.update(value);
        }, 'key conversion with invalid key should throw DataError');
    });
}, 'IDBCursor update() method with throwing/invalid keys');

// Static constructors on IDBKeyRange
['only', 'lowerBound', 'upperBound'].forEach(method => {
    test(t => {
        check_method(IDBKeyRange, method);
    }, 'IDBKeyRange ' + method + '() static with throwing/invalid keys');
});

test(t => {
    check_method(IDBKeyRange, 'bound', 2);
}, 'IDBKeyRange bound() static with throwing/invalid keys');

// Insertion methods on IDBObjectStore.
['add', 'put'].forEach(method => {
    indexeddb_upgrade_only_test((t, db) => {
        var out_of_line = db.createObjectStore('out-of-line keys');
        var in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'});
        var [key, err] = throwing_key('getter');
        assert_throws_exactly(err, () => {
            out_of_line[method]('value', key);
        }, 'key conversion with throwing getter should rethrow');

        assert_throws_dom('DataError', () => {
            out_of_line[method]('value', invalid_key);
        }, 'key conversion with invalid key should throw DataError');

        var value = {};
        [value.prop, err] = throwing_key('getter');
        assert_throws_exactly(err, () => {
            in_line[method](value);
        }, 'throwing getter should rethrow during clone');

        // Throwing from the getter during key conversion is
        // not possible since (1) a clone is used, (2) only own
        // properties are cloned, and (3) only own properties
        // are used for key path evaluation.

        value.prop = invalid_key;
        assert_throws_dom('DataError', () => {
            in_line[method](value);
        }, 'key conversion with invalid key should throw DataError');
    }, `IDBObjectStore ${method}() method with throwing/invalid keys`);
});

// Generic (key-or-key-path) methods on IDBObjectStore.
[
    'delete', 'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor',
    'openKeyCursor'
].forEach(method => {
    indexeddb_upgrade_only_test((t, db) => {
        var store = db.createObjectStore('store');

        check_method(store, method);
    }, `IDBObjectStore ${method}() method with throwing/invalid keys`);
});

// Generic (key-or-key-path) methods on IDBIndex.
[
    'get', 'getKey', 'getAll', 'getAllKeys', 'count', 'openCursor',
    'openKeyCursor'
].forEach(method => {
    indexeddb_upgrade_only_test((t, db) => {
        var store = db.createObjectStore('store');
        var index = store.createIndex('index', 'keyPath');

        check_method(index, method);
    }, `IDBIndex ${method}() method with throwing/invalid keys`);
});

</script>