|
| 1 | +from datetime import datetime |
| 2 | +import json |
| 3 | +import os |
| 4 | + |
| 5 | +from silicon import page |
| 6 | +from silicon.related import add as add_related |
| 7 | +from silicon.related import get as get_related |
| 8 | + |
| 9 | +# TODO: 'timestamp' in the JSONS needs to be 'revision' to match our |
| 10 | +# existing model. |
| 11 | + |
| 12 | + |
| 13 | +def test_export_command(app, runner): |
| 14 | + """ |
| 15 | + Tests the basic functionality of the 'export' command. |
| 16 | + It creates a page, runs export, and verifies the output file. |
| 17 | + """ |
| 18 | + revision = datetime.now().isoformat() |
| 19 | + with app.app_context(): |
| 20 | + page.write('export_page', 'This is the body.', revision=revision) |
| 21 | + add_related('export_page', 'related_page_a') |
| 22 | + add_related('export_page', 'related_page_b') |
| 23 | + result = runner.invoke(args=['export']) |
| 24 | + assert result.exit_code == 0, result.output |
| 25 | + |
| 26 | + export_dir = os.path.join(app.instance_path, 'export') |
| 27 | + exported_file = os.path.join(export_dir, 'export_page.json') |
| 28 | + assert os.path.exists(exported_file) |
| 29 | + |
| 30 | + with open(exported_file) as f: |
| 31 | + data = json.load(f) |
| 32 | + |
| 33 | + assert data['title'] == 'export_page' |
| 34 | + assert len(data['revisions']) == 1 |
| 35 | + assert data['revisions'][0]['body'] == 'This is the body.' |
| 36 | + assert data['revisions'][0]['revision'] == revision |
| 37 | + assert 'related_page_a' in data['attributes']['related'] |
| 38 | + assert 'related_page_b' in data['attributes']['related'] |
| 39 | + |
| 40 | + |
| 41 | +def test_import_fails_if_db_exists_without_force(app, runner): |
| 42 | + """ |
| 43 | + Tests that import command fails if the database exists |
| 44 | + and --force is not used. |
| 45 | + """ |
| 46 | + export_dir = os.path.join(app.instance_path, 'export') |
| 47 | + os.makedirs(export_dir, exist_ok=True) |
| 48 | + import_file = os.path.join(export_dir, 'dummy.json') |
| 49 | + with open(import_file, 'w') as f: |
| 50 | + json.dump({"title": "dummy", "revisions": []}, f) |
| 51 | + |
| 52 | + result = runner.invoke(args=['import']) |
| 53 | + assert result.exit_code != 0 |
| 54 | + |
| 55 | + |
| 56 | +def test_import_command(app, runner): |
| 57 | + """ |
| 58 | + Tests the basic functionality of the 'import' command. |
| 59 | + It creates a JSON file and runs import, then verifies the page was created. |
| 60 | + """ |
| 61 | + export_dir = os.path.join(app.instance_path, 'export') |
| 62 | + os.makedirs(export_dir, exist_ok=True) |
| 63 | + import_file = os.path.join(export_dir, 'import_page.json') |
| 64 | + revision = datetime.now().isoformat() |
| 65 | + |
| 66 | + import_data = { |
| 67 | + 'title': 'import_page', |
| 68 | + 'attributes': { |
| 69 | + 'related': ['related_page_a', 'related_page_b'] |
| 70 | + }, |
| 71 | + 'revisions': [ |
| 72 | + { |
| 73 | + 'revision': revision, |
| 74 | + 'body': 'Imported body.' |
| 75 | + } |
| 76 | + ] |
| 77 | + } |
| 78 | + with open(import_file, 'w') as f: |
| 79 | + json.dump(import_data, f) |
| 80 | + |
| 81 | + with app.app_context(): |
| 82 | + result = runner.invoke(args=['import', '--force']) |
| 83 | + assert result.exit_code == 0, result.output |
| 84 | + imported_page = page.read('import_page') |
| 85 | + related = get_related('import_page') |
| 86 | + |
| 87 | + assert imported_page['title'] == 'import_page' |
| 88 | + assert imported_page['body'] == 'Imported body.' |
| 89 | + assert imported_page['revision'] == revision |
| 90 | + assert 'related_page_a' in related |
| 91 | + assert 'related_page_b' in related |
| 92 | + |
| 93 | + |
| 94 | +def test_round_trip(app, runner): |
| 95 | + """ |
| 96 | + Tests that exporting and then importing a page preserves its content, |
| 97 | + especially with tricky characters. |
| 98 | + """ |
| 99 | + title = 'tricky_content_page' |
| 100 | + body = """ |
| 101 | + Here's some "tricky" content. |
| 102 | + 'Single quotes' are here. |
| 103 | + <Tags> and [brackets] and {braces}. |
| 104 | + Unicode: \u2603 SNOWMAN |
| 105 | +
|
| 106 | + Leading and trailing whitespace. |
| 107 | +
|
| 108 | + """ |
| 109 | + revision = datetime.now().isoformat() |
| 110 | + |
| 111 | + with app.app_context(): |
| 112 | + page.write(title, body, revision=revision) |
| 113 | + original_page = page.read(title) |
| 114 | + result = runner.invoke(args=['export']) |
| 115 | + assert result.exit_code == 0, result.output |
| 116 | + |
| 117 | + os.remove(os.path.join(app.instance_path, 'silicon.sqlite')) |
| 118 | + |
| 119 | + with app.app_context(): |
| 120 | + result = runner.invoke(args=['import']) |
| 121 | + assert result.exit_code == 0, result.output |
| 122 | + round_tripped_page = page.read(title) |
| 123 | + |
| 124 | + assert round_tripped_page['title'] == original_page['title'] |
| 125 | + assert round_tripped_page['body'] == original_page['body'] |
| 126 | + assert round_tripped_page['revision'] == original_page['revision'] |
| 127 | + |
| 128 | + |
| 129 | +def test_import_upsert(app, runner): |
| 130 | + """ |
| 131 | + Tests that importing a page with two identical revision strings works, and |
| 132 | + uses the most recent body version presented. (This tests the upsert |
| 133 | + functionality in page.py.) |
| 134 | + """ |
| 135 | + export_dir = os.path.join(app.instance_path, 'export') |
| 136 | + os.makedirs(export_dir, exist_ok=True) |
| 137 | + import_file = os.path.join(export_dir, 'import_page.json') |
| 138 | + revision = datetime.now().isoformat() |
| 139 | + |
| 140 | + import_data = { |
| 141 | + 'title': 'import_page', |
| 142 | + 'revisions': [ |
| 143 | + { |
| 144 | + 'revision': revision, |
| 145 | + 'body': 'First imported body.' |
| 146 | + }, |
| 147 | + { |
| 148 | + 'revision': revision, |
| 149 | + 'body': 'Second imported body.' |
| 150 | + } |
| 151 | + ] |
| 152 | + } |
| 153 | + with open(import_file, 'w') as f: |
| 154 | + json.dump(import_data, f) |
| 155 | + |
| 156 | + with app.app_context(): |
| 157 | + result = runner.invoke(args=['import', '--force']) |
| 158 | + assert result.exit_code == 0, result.output |
| 159 | + imported_page = page.read('import_page') |
| 160 | + |
| 161 | + assert imported_page['title'] == 'import_page' |
| 162 | + assert imported_page['body'] == 'Second imported body.' |
| 163 | + assert imported_page['revision'] == revision |
| 164 | + |
| 165 | + |
| 166 | +def test_export_fails_if_no_db(app, runner): |
| 167 | + """Tests that export command fails if the database does not exist.""" |
| 168 | + db_path = os.path.join(app.instance_path, 'silicon.sqlite') |
| 169 | + if os.path.exists(db_path): |
| 170 | + os.remove(db_path) |
| 171 | + |
| 172 | + result = runner.invoke(args=['export']) |
| 173 | + assert result.exit_code != 0 |
| 174 | + |
| 175 | + |
| 176 | +def test_import_with_malformed_json(app, runner): |
| 177 | + """Tests that import command handles malformed JSON files gracefully.""" |
| 178 | + export_dir = os.path.join(app.instance_path, 'export') |
| 179 | + os.makedirs(export_dir, exist_ok=True) |
| 180 | + import_file = os.path.join(export_dir, 'malformed.json') |
| 181 | + |
| 182 | + with open(import_file, 'w') as f: |
| 183 | + f.write('{"title": "malformed", "revisions": [') # Incomplete JSON |
| 184 | + |
| 185 | + db_path = os.path.join(app.instance_path, 'silicon.sqlite') |
| 186 | + if os.path.exists(db_path): |
| 187 | + os.remove(db_path) |
| 188 | + |
| 189 | + result = runner.invoke(args=['import']) |
| 190 | + assert result.exit_code != 0 |
0 commit comments