|
| 1 | +from collections import namedtuple |
| 2 | +from jinja2 import Template |
| 3 | +import os |
| 4 | +import sys |
| 5 | + |
| 6 | +Operation = namedtuple('Operation', ['operation_name', 'command_name', 'object', 'arguments']) |
| 7 | + |
| 8 | +CLIENT_OPERATIONS = [ |
| 9 | + Operation('listDatabases', 'listDatabases', 'client', ['filter: {}']), |
| 10 | + Operation('listDatabaseNames', 'listDatabases', 'client', ['filter: {}']), |
| 11 | + Operation('createChangeStream', 'aggregate', 'client', ['pipeline: []']) |
| 12 | +] |
| 13 | + |
| 14 | +DB_OPERATIONS = [ |
| 15 | + Operation('aggregate', 'aggregate', 'database', ['pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ]']), |
| 16 | + Operation('listCollections', 'listCollections', 'database', ['filter: {}']), |
| 17 | + Operation('listCollectionNames', 'listCollections', 'database', ['filter: {}']), |
| 18 | + Operation('runCommand', 'ping', 'database', ['command: { ping: 1 }']), |
| 19 | + Operation('createChangeStream', 'aggregate', 'database', ['pipeline: []']) |
| 20 | +] |
| 21 | + |
| 22 | +INSERT_MANY_ARGUMENTS = '''documents: |
| 23 | + - { x: 1 }''' |
| 24 | + |
| 25 | +BULK_WRITE_ARGUMENTS = '''requests: |
| 26 | + - insertOne: |
| 27 | + document: { _id: 1 }''' |
| 28 | + |
| 29 | +COLLECTION_OPERATIONS = [ |
| 30 | + Operation('aggregate', 'aggregate', 'collection', ['pipeline: []']), |
| 31 | + Operation('count', 'count', 'collection', ['filter: {}']), |
| 32 | + Operation('countDocuments', 'aggregate', 'collection', ['filter: {}']), |
| 33 | + Operation('estimatedDocumentCount', 'count', 'collection', []), |
| 34 | + Operation('distinct', 'distinct', 'collection', ['fieldName: x', 'filter: {}']), |
| 35 | + Operation('find', 'find', 'collection', ['filter: {}']), |
| 36 | + Operation('findOne', 'find', 'collection', ['filter: {}']), |
| 37 | + Operation('listIndexes', 'listIndexes', 'collection', []), |
| 38 | + Operation('listIndexNames', 'listIndexes', 'collection', []), |
| 39 | + Operation('createChangeStream', 'aggregate', 'collection', ['pipeline: []']), |
| 40 | + Operation('insertOne', 'insert', 'collection', ['document: { x: 1 }']), |
| 41 | + Operation('insertMany', 'insert', 'collection', [INSERT_MANY_ARGUMENTS]), |
| 42 | + Operation('deleteOne', 'delete', 'collection', ['filter: {}']), |
| 43 | + Operation('deleteMany', 'delete', 'collection', ['filter: {}']), |
| 44 | + Operation('replaceOne', 'update', 'collection', ['filter: {}', 'replacement: { x: 1 }']), |
| 45 | + Operation('updateOne', 'update', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), |
| 46 | + Operation('updateMany', 'update', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), |
| 47 | + Operation('findOneAndDelete', 'findAndModify', 'collection', ['filter: {}']), |
| 48 | + Operation('findOneAndReplace', 'findAndModify', 'collection', ['filter: {}', 'replacement: { x: 1 }']), |
| 49 | + Operation('findOneAndUpdate', 'findAndModify', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), |
| 50 | + Operation('bulkWrite', 'insert', 'collection', [BULK_WRITE_ARGUMENTS]), |
| 51 | + Operation('createIndex', 'createIndexes', 'collection', ['keys: { x: 1 }', 'name: "x_1"']), |
| 52 | + Operation('dropIndex', 'dropIndexes', 'collection', ['name: "x_1"']), |
| 53 | + Operation('dropIndexes', 'dropIndexes', 'collection', []), |
| 54 | +] |
| 55 | + |
| 56 | +# Session and GridFS operations are generally tested in other files, so they're not included in the list of all |
| 57 | +# operations. Individual generation functions can choose to include them if needed. |
| 58 | +OPERATIONS = CLIENT_OPERATIONS + DB_OPERATIONS + COLLECTION_OPERATIONS |
| 59 | + |
| 60 | +RETRYABLE_WRITE_OPERATIONS = [op for op in OPERATIONS if op.operation_name in |
| 61 | + ['insertOne', 'updateOne', 'deleteOne', 'replaceOne', 'findOneAndDelete', 'findOneAndUpdate', 'findOneAndReplace', 'insertMany', 'bulkWrite'] |
| 62 | +] |
| 63 | + |
| 64 | +RETRYABLE_READ_OPERATIONS = [op for op in OPERATIONS if op.operation_name in |
| 65 | + ['find', 'findOne', 'aggregate', 'distinct', 'count', 'estimatedDocumentCount', 'countDocuments', 'createChangeStream', 'listDatabases', |
| 66 | + 'listDatabaseNames', 'listCollections', 'listCollectionNames', 'listIndexes'] |
| 67 | +] |
| 68 | + |
| 69 | +templates_dir = sys.argv[1] |
| 70 | +tests_dir = sys.argv[2] |
| 71 | + |
| 72 | +def get_template(file): |
| 73 | + path = f'{templates_dir}/{file}.yml.template' |
| 74 | + return Template(open(path, 'r').read()) |
| 75 | + |
| 76 | +def write_yaml(file, template, injections): |
| 77 | + rendered = template.render(**injections) |
| 78 | + path = f'{tests_dir}/{file}.yml' |
| 79 | + open(path, 'w').write(rendered) |
| 80 | + |
| 81 | +def get_command_object(object): |
| 82 | + if object == 'client' or object == 'database': |
| 83 | + return 1 |
| 84 | + return '*collectionName' |
| 85 | + |
| 86 | +def max_time_supported(operation_name): |
| 87 | + return operation_name in ['aggregate', 'count', 'estimatedDocumentCount', 'distinct', 'find', 'findOne', |
| 88 | + 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'createIndex', 'dropIndex', 'dropIndexes'] |
| 89 | + |
| 90 | +def generate(name, operations): |
| 91 | + template = get_template(name) |
| 92 | + injections = { |
| 93 | + 'operations': operations, |
| 94 | + 'get_command_object': get_command_object, |
| 95 | + 'max_time_supported': max_time_supported, |
| 96 | + } |
| 97 | + write_yaml(name, template, injections) |
| 98 | + |
| 99 | +def generate_global_timeout_tests(): |
| 100 | + generate('global-timeoutMS', OPERATIONS) |
| 101 | + |
| 102 | +def generate_override_db(): |
| 103 | + generate('override-database-timeoutMS', DB_OPERATIONS + COLLECTION_OPERATIONS) |
| 104 | + |
| 105 | +def generate_override_coll(): |
| 106 | + generate('override-collection-timeoutMS', COLLECTION_OPERATIONS) |
| 107 | + |
| 108 | +def generate_override_operation(): |
| 109 | + generate('override-operation-timeoutMS', OPERATIONS) |
| 110 | + |
| 111 | +def generate_retryable(): |
| 112 | + generate('retryability-timeoutMS', RETRYABLE_WRITE_OPERATIONS + RETRYABLE_READ_OPERATIONS) |
| 113 | + generate('retryability-legacy-timeouts', RETRYABLE_WRITE_OPERATIONS + RETRYABLE_READ_OPERATIONS) |
| 114 | + |
| 115 | +def generate_deprecated(): |
| 116 | + generate('deprecated-options', OPERATIONS) |
| 117 | + |
| 118 | +generate_global_timeout_tests() |
| 119 | +generate_override_db() |
| 120 | +generate_override_coll() |
| 121 | +generate_override_operation() |
| 122 | +generate_retryable() |
| 123 | +generate_deprecated() |
0 commit comments