scriptorium 0.0.3 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.lt3 +324 -0
- data/README.md +3155 -1
- data/assets/.DS_Store +0 -0
- data/assets/README.md +44 -0
- data/assets/icons/social/reddit.png +0 -0
- data/assets/icons/social/x-logo.png +0 -0
- data/assets/icons/ui/.DS_Store +0 -0
- data/assets/icons/ui/back.png +0 -0
- data/assets/icons/ui/copy.png +0 -0
- data/assets/icons/ui/down.png +0 -0
- data/assets/icons/ui/end.png +0 -0
- data/assets/icons/ui/exit.png +0 -0
- data/assets/icons/ui/foo +10 -0
- data/assets/icons/ui/home.png +0 -0
- data/assets/icons/ui/left.png +0 -0
- data/assets/icons/ui/next.png +0 -0
- data/assets/icons/ui/right.png +0 -0
- data/assets/icons/ui/start.png +0 -0
- data/assets/icons/ui/up.png +0 -0
- data/assets/imagenotfound.jpg +0 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/sblog +84 -5
- data/bin/scriptorium +1 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
- data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
- data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
- data/doc/banner_svg_config.md +114 -0
- data/doc/contrib.lt3 +8 -0
- data/doc/dependencies.md +281 -0
- data/doc/hacker.lt3 +5 -0
- data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
- data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
- data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
- data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
- data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
- data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
- data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
- data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
- data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
- data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
- data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
- data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
- data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
- data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
- data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
- data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
- data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
- data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
- data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
- data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
- data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
- data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
- data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
- data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
- data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
- data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
- data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
- data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
- data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
- data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
- data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
- data/doc/imported/0018-ruby-puns/post.html +46 -0
- data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
- data/doc/imported/0020-fffff/metadata.txt +7 -0
- data/doc/imported/0020-fffff/post.html +24 -0
- data/doc/imported/0020-fffff/source.lt3 +12 -0
- data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
- data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
- data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
- data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
- data/doc/imported/0023-doctor-sleep/post.html +63 -0
- data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
- data/doc/imported/0024-just-a-test/metadata.txt +7 -0
- data/doc/imported/0024-just-a-test/post.html +24 -0
- data/doc/imported/0024-just-a-test/source.lt3 +12 -0
- data/doc/imported/import_summary.txt +98 -0
- data/doc/livetext-informal-spec.txt +65 -0
- data/doc/myuserdoc/ch-0.lt3 +31 -0
- data/doc/myuserdoc/ch-1.lt3 +37 -0
- data/doc/myuserdoc/ch-10.lt3 +22 -0
- data/doc/myuserdoc/ch-2.lt3 +37 -0
- data/doc/myuserdoc/ch-3.lt3 +19 -0
- data/doc/myuserdoc/ch-4.lt3 +43 -0
- data/doc/myuserdoc/ch-5.lt3 +22 -0
- data/doc/myuserdoc/ch-6.lt3 +19 -0
- data/doc/myuserdoc/ch-7.lt3 +16 -0
- data/doc/myuserdoc/ch-8.lt3 +13 -0
- data/doc/myuserdoc/ch-9.lt3 +19 -0
- data/doc/myuserdoc/tweak.rb +18 -0
- data/doc/myuserdoc/userdoc-toc.txt +88 -0
- data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
- data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
- data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
- data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
- data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
- data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
- data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
- data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
- data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
- data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
- data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
- data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
- data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
- data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
- data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
- data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
- data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
- data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
- data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
- data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
- data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
- data/doc/old-posts/0024-just-a-test.lt3 +12 -0
- data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
- data/doc/old-repo +1 -0
- data/doc/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +35 -0
- data/doc/user_guide_section_1.md +137 -0
- data/doc/user_guide_section_10.md +515 -0
- data/doc/user_guide_section_11.md +708 -0
- data/doc/user_guide_section_2.md +233 -0
- data/doc/user_guide_section_3.md +5 -0
- data/doc/user_guide_section_4.md +221 -0
- data/doc/user_guide_section_5.md +243 -0
- data/doc/user_guide_section_6.md +147 -0
- data/doc/user_guide_section_7.md +311 -0
- data/doc/user_guide_section_8.md +224 -0
- data/doc/user_guide_section_9.md +375 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +2373 -0
- data/lib/scriptorium/banner_svg.rb +729 -0
- data/lib/scriptorium/contract.rb +34 -0
- data/lib/scriptorium/exceptions.rb +201 -1
- data/lib/scriptorium/helpers.rb +675 -0
- data/lib/scriptorium/post.rb +259 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +938 -0
- data/lib/scriptorium/standard_files.rb +149 -0
- data/lib/scriptorium/support/bootstrap/css.txt +5 -0
- data/lib/scriptorium/support/bootstrap/js.txt +4 -0
- data/lib/scriptorium/support/common_js/clipboard.js +35 -0
- data/lib/scriptorium/support/common_js/content-loader.js +187 -0
- data/lib/scriptorium/support/common_js/navigation.js +52 -0
- data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
- data/lib/scriptorium/support/config/reddit.txt +10 -0
- data/lib/scriptorium/support/config/reddit_template.txt +17 -0
- data/lib/scriptorium/support/config/social.txt +8 -0
- data/lib/scriptorium/support/highlight/css.txt +2 -0
- data/lib/scriptorium/support/highlight/custom.css +119 -0
- data/lib/scriptorium/support/highlight/js.txt +1 -0
- data/lib/scriptorium/support/post_index/config.txt +15 -0
- data/lib/scriptorium/support/post_index/style.css +55 -0
- data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
- data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
- data/lib/scriptorium/support/templates/layout.txt +5 -0
- data/lib/scriptorium/support/templates/post.lt3 +104 -0
- data/lib/scriptorium/support/theme/footer.lt3 +2 -0
- data/lib/scriptorium/support/theme/header.lt3 +4 -0
- data/lib/scriptorium/support/theme/left.lt3 +3 -0
- data/lib/scriptorium/support/theme/main.lt3 +5 -0
- data/lib/scriptorium/support/theme/right.lt3 +3 -0
- data/lib/scriptorium/theme.rb +192 -0
- data/lib/scriptorium/version.rb +1 -1
- data/lib/scriptorium/view.rb +1021 -0
- data/lib/scriptorium/widgets/featured_posts.rb +149 -0
- data/lib/scriptorium/widgets/links.rb +112 -0
- data/lib/scriptorium/widgets/pages.rb +133 -0
- data/lib/scriptorium/widgets/widget.rb +133 -0
- data/lib/scriptorium.rb +38 -34
- data/lib/skeleton.rb +10 -1
- data/scriptorium.gemspec +17 -5
- data/test/README.md +69 -0
- data/test/WEB_INTEGRATION_README.md +196 -0
- data/test/all +83 -0
- data/test/api_demo.rb +99 -0
- data/test/assets/imagenotfound.jpg +0 -0
- data/test/assets/images/.DS_Store +0 -0
- data/test/assets/images/README.md +27 -0
- data/test/assets/images/odd_aspect.png +0 -0
- data/test/assets/images/perfect.png +0 -0
- data/test/assets/images/small.png +0 -0
- data/test/assets/images/tall.png +0 -0
- data/test/assets/images/very_tall.png +0 -0
- data/test/assets/images/very_wide.png +0 -0
- data/test/assets/images/wide.png +0 -0
- data/test/assets/testbanner.jpg +0 -0
- data/test/banner_svg/simple_helpers.rb +13 -0
- data/test/banner_svg/unit.rb +1000 -0
- data/test/config/deployment.txt +5 -0
- data/test/ed_test.rb +204 -0
- data/test/integration/cursor_banner_combinations.rb +193 -0
- data/test/integration/cursor_banner_features.rb +374 -0
- data/test/integration/integration_test.rb +326 -0
- data/test/integration/preview_flow_test.rb +94 -0
- data/test/livetext_plugin_test.rb +500 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/svg.txt +3 -0
- data/test/manual/banner-tests/test01.html +122 -0
- data/test/manual/banner-tests/test02.html +122 -0
- data/test/manual/banner-tests/test03.html +122 -0
- data/test/manual/banner-tests/test04.html +129 -0
- data/test/manual/banner-tests/test05.html +129 -0
- data/test/manual/banner-tests/test06.html +129 -0
- data/test/manual/banner-tests/test07.html +129 -0
- data/test/manual/banner-tests/test08.html +123 -0
- data/test/manual/banner-tests/test09.html +123 -0
- data/test/manual/banner-tests/test10.html +123 -0
- data/test/manual/banner-tests/test11.html +123 -0
- data/test/manual/banner-tests/test12.html +123 -0
- data/test/manual/banner-tests/test13.html +123 -0
- data/test/manual/banner-tests/test14.html +123 -0
- data/test/manual/banner-tests/test15.html +122 -0
- data/test/manual/banner-tests/test16.html +122 -0
- data/test/manual/banner-tests/test17.html +122 -0
- data/test/manual/banner-tests/test18.html +132 -0
- data/test/manual/banner-tests/test19.html +132 -0
- data/test/manual/banner-tests/test20.html +132 -0
- data/test/manual/banner-tests/test21.html +132 -0
- data/test/manual/banner-tests/test22.html +132 -0
- data/test/manual/banner-tests/test23.html +132 -0
- data/test/manual/banner-tests/test24.html +132 -0
- data/test/manual/banner-tests/test25.html +131 -0
- data/test/manual/banner_environment.rb +205 -0
- data/test/manual/codemirror_demo.html +773 -0
- data/test/manual/create_posts_for_web.rb +114 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/preview_manual_test.rb +129 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/test_advanced_widgets.rb +73 -0
- data/test/manual/test_banner_combinations.rb +120 -0
- data/test/manual/test_banner_features.rb +306 -0
- data/test/manual/test_banner_integration.rb +115 -0
- data/test/manual/test_banner_radial.rb +87 -0
- data/test/manual/test_basic_posts.rb +47 -0
- data/test/manual/test_layout_widgets.rb +40 -0
- data/test/manual/test_pagination.rb +24 -0
- data/test/manual/test_random_posts.rb +38 -0
- data/test/manual/test_syntax_highlighting.rb +167 -0
- data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
- data/test/rubytext/rubytext_demo_test.rb +42 -0
- data/test/rubytext/rubytext_testing_guide.md +277 -0
- data/test/run_automated_tests.rb +45 -0
- data/test/staging/.DS_Store +0 -0
- data/test/support/preview_utils.rb +88 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_gem_assets.rb +48 -0
- data/test/test_helpers.rb +240 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +883 -0
- data/test/unit/api.rb +1776 -0
- data/test/unit/asset_management.rb +219 -0
- data/test/unit/backup_test.rb +451 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +69 -0
- data/test/unit/core.rb +1211 -0
- data/test/unit/deploy_config_test.rb +248 -0
- data/test/unit/deploy_test.rb +478 -0
- data/test/unit/edit_post_test.rb +168 -0
- data/test/unit/gem_asset_management.rb +183 -0
- data/test/unit/livetext_basic.rb +57 -0
- data/test/unit/livetext_compatibility.rb +82 -0
- data/test/unit/parse_cmd_test.rb +260 -0
- data/test/unit/permalink_copy_test.rb +211 -0
- data/test/unit/post.rb +309 -0
- data/test/unit/post_index_config_test.rb +258 -0
- data/test/unit/post_state_helpers_test.rb +137 -0
- data/test/unit/read_commented_file_test.rb +278 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +569 -0
- data/test/unit/social_test.rb +366 -0
- data/test/unit/syntax_highlighting.rb +70 -0
- data/test/unit/theme_management_test.rb +91 -0
- data/test/unit/view.rb +498 -0
- data/test/unit/widgets.rb +669 -0
- data/test/web_integration_test.rb +231 -0
- data/test/web_test_helper.rb +218 -0
- data/test/web_workflow_test.rb +527 -0
- data/test/wizard_test.rb +123 -0
- data/ui/README.md +67 -0
- data/ui/common/lib/ui_common.rb +8 -0
- data/ui/rubytext/README.md +191 -0
- data/ui/rubytext/bin/scriptorium-rubytext +402 -0
- data/ui/rubytext/lib/rubytext_ui.rb +300 -0
- data/ui/tui/bin/scriptorium +1890 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +2600 -0
- data/ui/web/app/assets/livetext_mode.js +244 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +196 -0
- data/ui/web/app/views/asset_management.erb +645 -0
- data/ui/web/app/views/backup_management.erb +238 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/config_widget.erb +232 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +154 -0
- data/ui/web/app/views/deploy_config.erb +149 -0
- data/ui/web/app/views/edit_pages.erb +363 -0
- data/ui/web/app/views/edit_post.erb +175 -0
- data/ui/web/app/views/edit_theme.erb +73 -0
- data/ui/web/app/views/edit_theme_file.erb +74 -0
- data/ui/web/app/views/error_page.erb +29 -0
- data/ui/web/app/views/header_config.erb +155 -0
- data/ui/web/app/views/layout_config.erb +147 -0
- data/ui/web/app/views/navbar_config.erb +411 -0
- data/ui/web/app/views/theme_management.erb +130 -0
- data/ui/web/app/views/view_dashboard.erb +779 -0
- data/ui/web/app/views/widgets.erb +249 -0
- data/ui/web/bin/scriptorium-web +164 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/timing.log +17 -0
- data/ui/web/tmp/web_server.log +0 -0
- metadata +434 -8
- data/lib/scriptorium/engine.rb +0 -22
- data/test/engine/unit.rb +0 -44
data/test/unit/api.rb
ADDED
@@ -0,0 +1,1776 @@
|
|
1
|
+
# test/unit/api.rb
|
2
|
+
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'open3'
|
6
|
+
require_relative '../../lib/scriptorium'
|
7
|
+
require_relative '../test_helpers'
|
8
|
+
|
9
|
+
class TestScriptoriumAPI < Minitest::Test
|
10
|
+
include Scriptorium::Exceptions
|
11
|
+
include Scriptorium::Helpers
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
|
17
|
+
@api = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@test_dir = "test/scriptorium-TEST"
|
22
|
+
# Clean up any existing test directory first
|
23
|
+
FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
|
24
|
+
@api = Scriptorium::API.new(testmode: true)
|
25
|
+
@api.create_repo(@test_dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Basic API functionality tests
|
29
|
+
|
30
|
+
def test_001_api_initialization
|
31
|
+
assert_instance_of Scriptorium::API, @api
|
32
|
+
assert_instance_of Scriptorium::Repo, @api.repo
|
33
|
+
refute_nil @api.current_view
|
34
|
+
assert_equal "sample", @api.current_view.name
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_002_create_view
|
38
|
+
@api.create_view("test_view", "Test View", "A test view")
|
39
|
+
|
40
|
+
assert_equal "test_view", @api.current_view.name
|
41
|
+
assert_equal "Test View", @api.current_view.title
|
42
|
+
assert_equal "A test view", @api.current_view.subtitle
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_003_create_post
|
46
|
+
@api.create_view("test_view", "Test View")
|
47
|
+
post = @api.create_post("Test Post", "Test body", tags: ["test"])
|
48
|
+
|
49
|
+
assert_instance_of Scriptorium::Post, post
|
50
|
+
assert_equal "Test Post", post.title
|
51
|
+
assert_equal "test", post.tags
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_004_create_page
|
55
|
+
@api.create_view("test_view", "Test View")
|
56
|
+
|
57
|
+
page_name = @api.create_page("test_view", "about", "About Us", "This is our about page content.")
|
58
|
+
|
59
|
+
assert_equal "about", page_name
|
60
|
+
|
61
|
+
# Check that the page file was created
|
62
|
+
page_file = "test/scriptorium-TEST/views/test_view/pages/about.lt3"
|
63
|
+
assert File.exist?(page_file), "Page file should exist"
|
64
|
+
|
65
|
+
# Check the content
|
66
|
+
content = read_file(page_file)
|
67
|
+
assert_includes content, ".title About Us"
|
68
|
+
assert_includes content, "This is our about page content."
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_005_posts
|
72
|
+
@api.create_view("test_view", "Test View")
|
73
|
+
@api.create_post("Post 1", "Body 1")
|
74
|
+
@api.create_post("Post 2", "Body 2")
|
75
|
+
|
76
|
+
posts = @api.posts
|
77
|
+
assert_equal 2, posts.length
|
78
|
+
# Posts might not be in creation order, so check both exist
|
79
|
+
titles = posts.map(&:title)
|
80
|
+
assert_includes titles, "Post 1"
|
81
|
+
assert_includes titles, "Post 2"
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_005_post
|
85
|
+
@api.create_view("test_view", "Test View")
|
86
|
+
created_post = @api.create_post("Test Post", "Test body")
|
87
|
+
|
88
|
+
retrieved_post = @api.post(created_post.id)
|
89
|
+
assert_equal created_post.id, retrieved_post.id
|
90
|
+
assert_equal "Test Post", retrieved_post.title
|
91
|
+
end
|
92
|
+
|
93
|
+
# New API methods tests
|
94
|
+
def test_006_views
|
95
|
+
@api.create_view("view1", "View 1")
|
96
|
+
@api.create_view("view2", "View 2")
|
97
|
+
|
98
|
+
views = @api.views.map(&:name)
|
99
|
+
assert_includes views, "view1"
|
100
|
+
assert_includes views, "view2"
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_007_post_attrs
|
104
|
+
@api.create_view("test_view", "Test View")
|
105
|
+
post = @api.create_post("Test Post", "Test body", tags: ["test", "demo"])
|
106
|
+
|
107
|
+
attrs = @api.post_attrs(post.id, :title, :tags)
|
108
|
+
assert_equal ["Test Post", "test, demo"], attrs
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_008_post_attrs_with_post_object
|
112
|
+
@api.create_view("test_view", "Test View")
|
113
|
+
post = @api.create_post("Test Post", "Test body", tags: ["test"])
|
114
|
+
|
115
|
+
attrs = @api.post_attrs(post, :title, :tags)
|
116
|
+
assert_equal ["Test Post", "test"], attrs
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_009_views_for
|
120
|
+
@api.create_view("test_view", "Test View")
|
121
|
+
post = @api.create_post("Test Post", "Test body")
|
122
|
+
|
123
|
+
views = @api.views_for(post)
|
124
|
+
assert_equal ["test_view"], views
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_010_views_for_with_id
|
128
|
+
@api.create_view("test_view", "Test View")
|
129
|
+
post = @api.create_post("Test Post", "Test body")
|
130
|
+
|
131
|
+
views = @api.views_for(post.id)
|
132
|
+
assert_equal ["test_view"], views
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_011_apply_theme
|
136
|
+
@api.create_view("test_view", "Test View")
|
137
|
+
|
138
|
+
# Should not raise an error
|
139
|
+
@api.apply_theme("standard")
|
140
|
+
assert_equal "standard", @api.current_view.theme
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
# Empty methods tests (should not raise errors)
|
146
|
+
def test_012_empty_methods
|
147
|
+
assert_equal [], @api.drafts
|
148
|
+
# Widgets are now available from widgets.txt
|
149
|
+
widgets = @api.widgets_available
|
150
|
+
assert_includes widgets, "links"
|
151
|
+
assert_includes widgets, "pages"
|
152
|
+
|
153
|
+
# These should not raise errors
|
154
|
+
# @api.delete_draft("some_path") # This would fail since it's not a valid draft path
|
155
|
+
# @api.delete_post(1) # This would fail since post doesn't exist
|
156
|
+
# @api.generate_widget("some_widget") # This would fail since no current view set
|
157
|
+
@api.select_posts { |p| true }
|
158
|
+
@api.search_posts(title: "query")
|
159
|
+
# @api.unlink_post(nil, nil) # This would fail since no current view and invalid post
|
160
|
+
# @api.generate_all # This would fail since no current view set
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_013_drafts
|
164
|
+
drafts = @api.drafts
|
165
|
+
assert_instance_of Array, drafts
|
166
|
+
# Should return empty array if no drafts directory exists
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_014_themes_available
|
170
|
+
themes = @api.themes_available
|
171
|
+
assert_instance_of Array, themes
|
172
|
+
|
173
|
+
# Should have the standard theme
|
174
|
+
assert_includes themes, "standard"
|
175
|
+
|
176
|
+
# Check system vs user themes
|
177
|
+
system_themes = @api.system_themes
|
178
|
+
user_themes = @api.user_themes
|
179
|
+
|
180
|
+
assert_includes system_themes, "standard"
|
181
|
+
assert_empty user_themes # No user themes yet
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_015_clone_theme
|
185
|
+
# Clone the standard theme
|
186
|
+
result = @api.clone_theme("standard", "my-custom")
|
187
|
+
assert_equal "my-custom", result
|
188
|
+
|
189
|
+
# Check that the new theme exists
|
190
|
+
themes = @api.themes_available
|
191
|
+
assert_includes themes, "my-custom"
|
192
|
+
|
193
|
+
# Check that it's now a user theme
|
194
|
+
user_themes = @api.user_themes
|
195
|
+
assert_includes user_themes, "my-custom"
|
196
|
+
|
197
|
+
# Check that standard is still a system theme
|
198
|
+
system_themes = @api.system_themes
|
199
|
+
assert_includes system_themes, "standard"
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_016_clone_theme_validation
|
203
|
+
# Try to clone to existing theme name
|
204
|
+
assert_raises(ThemeAlreadyExists) do
|
205
|
+
@api.clone_theme("standard", "standard")
|
206
|
+
end
|
207
|
+
|
208
|
+
# Try to clone from non-existent theme
|
209
|
+
assert_raises(ThemeNotFound) do
|
210
|
+
@api.clone_theme("nonexistent", "new-theme")
|
211
|
+
end
|
212
|
+
|
213
|
+
# Try to clone with invalid name
|
214
|
+
assert_raises(ThemeNameInvalid) do
|
215
|
+
@api.clone_theme("standard", "invalid name with spaces")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_017_widgets_available
|
220
|
+
widgets = @api.widgets_available
|
221
|
+
assert_instance_of Array, widgets
|
222
|
+
# Should return available widgets from widgets.txt
|
223
|
+
assert_includes widgets, "links"
|
224
|
+
assert_includes widgets, "pages"
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_016_generate_view
|
228
|
+
@api.create_view("test_view", "Test View")
|
229
|
+
|
230
|
+
# Should not raise an error
|
231
|
+
@api.generate_view
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_017_generate_view_with_specific_view
|
235
|
+
@api.create_view("view1", "View 1")
|
236
|
+
@api.create_view("view2", "View 2")
|
237
|
+
|
238
|
+
# Should not raise an error
|
239
|
+
@api.generate_view("view1")
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
# Error handling tests
|
245
|
+
def test_018_create_post_without_view
|
246
|
+
# Clear the current view directly
|
247
|
+
@api.repo.instance_variable_set(:@current_view, nil)
|
248
|
+
|
249
|
+
assert_raises(ViewTargetNil) do
|
250
|
+
@api.create_post("Test Post", "Test body")
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_019_safe_delete_post
|
255
|
+
@api.create_view("test_view", "Test View")
|
256
|
+
post = @api.create_post("Test Post", "Test body")
|
257
|
+
|
258
|
+
# Initially visible
|
259
|
+
posts = @api.posts
|
260
|
+
assert_equal 1, posts.length
|
261
|
+
|
262
|
+
# Delete the post
|
263
|
+
@api.delete_post(post.id)
|
264
|
+
|
265
|
+
# Should not appear in posts list
|
266
|
+
posts = @api.posts
|
267
|
+
assert_equal 0, posts.length
|
268
|
+
|
269
|
+
# But post object still exists and can be retrieved
|
270
|
+
retrieved_post = @api.post(post.id)
|
271
|
+
assert_equal "Test Post", retrieved_post.title
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_020_undelete_post
|
275
|
+
@api.create_view("test_view", "Test View")
|
276
|
+
post = @api.create_post("Test Post", "Test body")
|
277
|
+
|
278
|
+
# Delete the post
|
279
|
+
@api.delete_post(post.id)
|
280
|
+
assert_equal 0, @api.posts.length
|
281
|
+
|
282
|
+
# Undelete the post
|
283
|
+
@api.undelete_post(post.id)
|
284
|
+
|
285
|
+
# Should appear in posts list again
|
286
|
+
posts = @api.posts
|
287
|
+
assert_equal 1, posts.length
|
288
|
+
assert_equal "Test Post", posts[0].title
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_021_update_post
|
292
|
+
@api.create_view("test_view", "Test View")
|
293
|
+
@api.create_view("other_view", "Other View")
|
294
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view", "other_view"])
|
295
|
+
|
296
|
+
# Update the views field
|
297
|
+
result = @api.update_post(post.id, {views: ["test_view"]})
|
298
|
+
assert result
|
299
|
+
|
300
|
+
# Check that the source file was updated
|
301
|
+
source_file = post.dir/"source.lt3"
|
302
|
+
content = read_file(source_file)
|
303
|
+
assert_includes content, ".views test_view"
|
304
|
+
assert_includes content, "# updated views"
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_022_update_post_preserves_comments
|
308
|
+
@api.create_view("test_view", "Test View")
|
309
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
310
|
+
|
311
|
+
# Manually add a comment to the source file
|
312
|
+
source_file = post.dir/"source.lt3"
|
313
|
+
lines = read_file(source_file, lines: true, chomp: false)
|
314
|
+
lines.map! do |line|
|
315
|
+
if line.strip.start_with?('.views')
|
316
|
+
".views test_view # original comment\n"
|
317
|
+
else
|
318
|
+
line
|
319
|
+
end
|
320
|
+
end
|
321
|
+
write_file(source_file, lines.join)
|
322
|
+
|
323
|
+
# Update the views field
|
324
|
+
result = @api.update_post(post.id, {views: ["new_view"]})
|
325
|
+
assert result
|
326
|
+
|
327
|
+
# Check that original comment is preserved
|
328
|
+
content = read_file(source_file)
|
329
|
+
assert_includes content, "# original comment"
|
330
|
+
assert_includes content, "# updated views"
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_023_update_post_multiple_fields
|
334
|
+
@api.create_view("test_view", "Test View")
|
335
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
336
|
+
|
337
|
+
# Update multiple fields at once
|
338
|
+
result = @api.update_post(post.id, {
|
339
|
+
title: "Updated Title",
|
340
|
+
tags: ["new", "tags"]
|
341
|
+
})
|
342
|
+
assert result
|
343
|
+
|
344
|
+
# Check that both fields were updated
|
345
|
+
source_file = post.dir/"source.lt3"
|
346
|
+
content = read_file(source_file)
|
347
|
+
assert_includes content, ".title Updated Title"
|
348
|
+
assert_includes content, ".tags new, tags"
|
349
|
+
assert_includes content, "# updated title"
|
350
|
+
assert_includes content, "# updated tags"
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
|
355
|
+
def test_024_unlink_post
|
356
|
+
@api.create_view("test_view", "Test View")
|
357
|
+
@api.create_view("other_view", "Other View")
|
358
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view", "other_view"])
|
359
|
+
|
360
|
+
# Initially post should be in both views
|
361
|
+
assert_includes post.views, "test_view"
|
362
|
+
assert_includes post.views, "other_view"
|
363
|
+
|
364
|
+
# Unlink from current view
|
365
|
+
result = @api.unlink_post(post.id)
|
366
|
+
assert result
|
367
|
+
|
368
|
+
# Post should now only be in test_view (since we unlinked from other_view)
|
369
|
+
updated_post = @api.post(post.id)
|
370
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
371
|
+
assert_includes updated_views, "test_view"
|
372
|
+
refute_includes updated_views, "other_view"
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_025_link_post
|
376
|
+
@api.create_view("test_view", "Test View")
|
377
|
+
@api.create_view("other_view", "Other View")
|
378
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
379
|
+
|
380
|
+
# Initially post should only be in test_view
|
381
|
+
assert_includes post.views, "test_view"
|
382
|
+
refute_includes post.views, "other_view"
|
383
|
+
|
384
|
+
# Link to other_view
|
385
|
+
result = @api.link_post(post.id, "other_view")
|
386
|
+
assert result
|
387
|
+
|
388
|
+
# Post should now be in both views
|
389
|
+
updated_post = @api.post(post.id)
|
390
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
391
|
+
assert_includes updated_views, "test_view"
|
392
|
+
assert_includes updated_views, "other_view"
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_026_link_post_current_view
|
396
|
+
@api.create_view("test_view", "Test View")
|
397
|
+
@api.create_view("other_view", "Other View")
|
398
|
+
post = @api.create_post("Test Post", "Test body", views: ["other_view"])
|
399
|
+
|
400
|
+
# Initially post should only be in other_view
|
401
|
+
assert_includes post.views, "other_view"
|
402
|
+
refute_includes post.views, "test_view"
|
403
|
+
|
404
|
+
# Set current view to test_view
|
405
|
+
@api.view("test_view")
|
406
|
+
|
407
|
+
# Link to current view (test_view)
|
408
|
+
result = @api.link_post(post.id)
|
409
|
+
assert result
|
410
|
+
|
411
|
+
# Post should now be in both views
|
412
|
+
updated_post = @api.post(post.id)
|
413
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
414
|
+
assert_includes updated_views, "test_view"
|
415
|
+
assert_includes updated_views, "other_view"
|
416
|
+
end
|
417
|
+
|
418
|
+
def test_027_link_post_duplicate
|
419
|
+
@api.create_view("test_view", "Test View")
|
420
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
421
|
+
|
422
|
+
# Initially post should be in test_view
|
423
|
+
assert_includes post.views, "test_view"
|
424
|
+
|
425
|
+
# Try to link to the same view (should not add duplicate)
|
426
|
+
result = @api.link_post(post.id, "test_view")
|
427
|
+
assert result
|
428
|
+
|
429
|
+
# Post should still only be in test_view (no duplicates)
|
430
|
+
updated_post = @api.post(post.id)
|
431
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
432
|
+
assert_equal ["test_view"], updated_views
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_028_post_add_view
|
436
|
+
@api.create_view("test_view", "Test View")
|
437
|
+
@api.create_view("other_view", "Other View")
|
438
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
439
|
+
|
440
|
+
# Initially post should only be in test_view
|
441
|
+
assert_includes post.views, "test_view"
|
442
|
+
refute_includes post.views, "other_view"
|
443
|
+
|
444
|
+
# Add other_view to the post
|
445
|
+
result = @api.post_add_view(post.id, "other_view")
|
446
|
+
assert result
|
447
|
+
|
448
|
+
# Post should now be in both views
|
449
|
+
updated_post = @api.post(post.id)
|
450
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
451
|
+
assert_includes updated_views, "test_view"
|
452
|
+
assert_includes updated_views, "other_view"
|
453
|
+
end
|
454
|
+
|
455
|
+
def test_029_post_add_view_with_view_object
|
456
|
+
@api.create_view("test_view", "Test View")
|
457
|
+
@api.create_view("other_view", "Other View")
|
458
|
+
other_view = @api.view("other_view") # Get the View object
|
459
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view"])
|
460
|
+
|
461
|
+
# Add other_view to the post using View object
|
462
|
+
result = @api.post_add_view(post.id, other_view)
|
463
|
+
assert result
|
464
|
+
|
465
|
+
# Post should now be in both views
|
466
|
+
updated_post = @api.post(post.id)
|
467
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
468
|
+
assert_includes updated_views, "test_view"
|
469
|
+
assert_includes updated_views, "other_view"
|
470
|
+
end
|
471
|
+
|
472
|
+
def test_030_post_remove_view
|
473
|
+
@api.create_view("test_view", "Test View")
|
474
|
+
@api.create_view("other_view", "Other View")
|
475
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view", "other_view"])
|
476
|
+
|
477
|
+
# Initially post should be in both views
|
478
|
+
assert_includes post.views, "test_view"
|
479
|
+
assert_includes post.views, "other_view"
|
480
|
+
|
481
|
+
# Remove other_view from the post
|
482
|
+
result = @api.post_remove_view(post.id, "other_view")
|
483
|
+
assert result
|
484
|
+
|
485
|
+
# Post should now only be in test_view
|
486
|
+
updated_post = @api.post(post.id)
|
487
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
488
|
+
assert_includes updated_views, "test_view"
|
489
|
+
refute_includes updated_views, "other_view"
|
490
|
+
end
|
491
|
+
|
492
|
+
def test_031_post_remove_view_with_view_object
|
493
|
+
@api.create_view("test_view", "Test View")
|
494
|
+
@api.create_view("other_view", "Other View")
|
495
|
+
other_view = @api.view("other_view") # Get the View object
|
496
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view", "other_view"])
|
497
|
+
|
498
|
+
# Remove other_view from the post using View object
|
499
|
+
result = @api.post_remove_view(post.id, other_view)
|
500
|
+
assert result
|
501
|
+
|
502
|
+
# Post should now only be in test_view
|
503
|
+
updated_post = @api.post(post.id)
|
504
|
+
updated_views = updated_post.views.strip.split(/\s+/)
|
505
|
+
assert_includes updated_views, "test_view"
|
506
|
+
refute_includes updated_views, "other_view"
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_032_update_post_blurb
|
510
|
+
@api.create_view("test_view", "Test View")
|
511
|
+
post = @api.create_post("Test Post", "Test body")
|
512
|
+
|
513
|
+
# Manually add a blurb line to the source file
|
514
|
+
source_file = post.dir/"source.lt3"
|
515
|
+
lines = read_file(source_file, lines: true, chomp: false)
|
516
|
+
lines.insert(-2, ".blurb This is just a short intro to this post.\n") # Insert before the body
|
517
|
+
write_file(source_file, lines.join)
|
518
|
+
|
519
|
+
# Update the blurb
|
520
|
+
result = @api.update_post(post.id, {blurb: "Updated blurb for this post"})
|
521
|
+
assert result
|
522
|
+
|
523
|
+
# Check that the blurb was updated
|
524
|
+
content = read_file(source_file)
|
525
|
+
assert_includes content, ".blurb Updated blurb for this post"
|
526
|
+
assert_includes content, "# updated blurb"
|
527
|
+
end
|
528
|
+
|
529
|
+
def test_033_delete_draft
|
530
|
+
@api.create_view("test_view", "Test View")
|
531
|
+
|
532
|
+
# Create a draft
|
533
|
+
draft_path = @api.draft(title: "Test Draft", body: "Test body")
|
534
|
+
|
535
|
+
# Verify draft exists
|
536
|
+
drafts = @api.drafts
|
537
|
+
assert_equal 1, drafts.length
|
538
|
+
assert_equal draft_path, drafts.first[:path]
|
539
|
+
|
540
|
+
# Delete the draft
|
541
|
+
result = @api.delete_draft(draft_path)
|
542
|
+
assert result
|
543
|
+
|
544
|
+
# Verify draft is gone
|
545
|
+
drafts = @api.drafts
|
546
|
+
assert_equal 0, drafts.length
|
547
|
+
end
|
548
|
+
|
549
|
+
def test_034_delete_draft_invalid_path
|
550
|
+
@api.create_view("test_view", "Test View")
|
551
|
+
|
552
|
+
# Test with non-draft file
|
553
|
+
assert_raises(DraftFileInvalid) do
|
554
|
+
@api.delete_draft("not-a-draft.txt")
|
555
|
+
end
|
556
|
+
|
557
|
+
# Test with non-existent file
|
558
|
+
assert_raises(DraftFileNotFound) do
|
559
|
+
@api.delete_draft("nonexistent-draft.lt3")
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def test_035_generate_view
|
564
|
+
@api.create_view("test_view", "Test View")
|
565
|
+
@api.create_post("Test Post", "Test body")
|
566
|
+
|
567
|
+
# Should not raise an error
|
568
|
+
result = @api.generate_view
|
569
|
+
assert result
|
570
|
+
end
|
571
|
+
|
572
|
+
|
573
|
+
|
574
|
+
def test_036_generate_widget
|
575
|
+
@api.create_view("test_view", "Test View")
|
576
|
+
|
577
|
+
# Create the widget directory and sample data
|
578
|
+
widget_dir = @api.repo.root/:views/"test_view"/:widgets/"links"
|
579
|
+
make_dir(widget_dir)
|
580
|
+
write_file(widget_dir/"list.txt", "https://example.com, Example Link")
|
581
|
+
|
582
|
+
# Should not raise an error for a valid widget
|
583
|
+
result = @api.generate_widget("links")
|
584
|
+
assert result
|
585
|
+
|
586
|
+
# Verify the widget files were created
|
587
|
+
assert File.exist?(widget_dir/"links-card.html")
|
588
|
+
end
|
589
|
+
|
590
|
+
|
591
|
+
|
592
|
+
def test_037_generate_widget_invalid_name
|
593
|
+
@api.create_view("test_view", "Test View")
|
594
|
+
|
595
|
+
# Test with invalid widget name
|
596
|
+
assert_raises(WidgetNameInvalid) do
|
597
|
+
@api.generate_widget("invalid-widget")
|
598
|
+
end
|
599
|
+
|
600
|
+
# Test with nil
|
601
|
+
assert_raises(WidgetNameNil) do
|
602
|
+
@api.generate_widget(nil)
|
603
|
+
end
|
604
|
+
|
605
|
+
# Test with empty string
|
606
|
+
assert_raises(WidgetsArgEmpty) do
|
607
|
+
@api.generate_widget("")
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def test_038_generate_widget_nonexistent
|
612
|
+
@api.create_view("test_view", "Test View")
|
613
|
+
|
614
|
+
# Test with non-existent widget class
|
615
|
+
assert_raises(CannotBuildWidget) do
|
616
|
+
@api.generate_widget("nonexistent")
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
def test_039_select_posts
|
621
|
+
@api.create_view("test_view", "Test View")
|
622
|
+
@api.create_view("other_view", "Other View")
|
623
|
+
|
624
|
+
# Create posts in different views
|
625
|
+
post1 = @api.create_post("Post 1", "Body 1", views: ["test_view"])
|
626
|
+
post2 = @api.create_post("Post 2", "Body 2", views: ["other_view"])
|
627
|
+
post3 = @api.create_post("Post 3", "Body 3", views: ["test_view", "other_view"])
|
628
|
+
|
629
|
+
# Test filtering by view
|
630
|
+
test_view_posts = @api.select_posts { |post| post.views.include?("test_view") }
|
631
|
+
assert_equal 2, test_view_posts.length
|
632
|
+
assert_includes test_view_posts.map(&:title), "Post 1"
|
633
|
+
assert_includes test_view_posts.map(&:title), "Post 3"
|
634
|
+
|
635
|
+
# Test filtering by title
|
636
|
+
title_posts = @api.select_posts { |post| post.title.include?("Post 2") }
|
637
|
+
assert_equal 1, title_posts.length
|
638
|
+
assert_equal "Post 2", title_posts.first.title
|
639
|
+
end
|
640
|
+
|
641
|
+
def test_040_search_posts
|
642
|
+
@api.create_view("test_view", "Test View")
|
643
|
+
|
644
|
+
# Create posts with different content
|
645
|
+
post1 = @api.create_post("Ruby Programming", "Learn Ruby basics", tags: "ruby, programming")
|
646
|
+
post2 = @api.create_post("Python Guide", "Python vs Ruby", tags: "python, comparison")
|
647
|
+
post3 = @api.create_post("Scriptorium API", "API documentation", tags: "api, scriptorium")
|
648
|
+
|
649
|
+
# Test title search with regex
|
650
|
+
ruby_posts = @api.search_posts(title: /Ruby/)
|
651
|
+
assert_equal 1, ruby_posts.length
|
652
|
+
assert_equal "Ruby Programming", ruby_posts.first.title
|
653
|
+
|
654
|
+
# Test body search with string
|
655
|
+
body_posts = @api.search_posts(body: "Ruby")
|
656
|
+
assert_equal 2, body_posts.length
|
657
|
+
assert_includes body_posts.map(&:title), "Ruby Programming"
|
658
|
+
assert_includes body_posts.map(&:title), "Python Guide"
|
659
|
+
|
660
|
+
# Test tags search
|
661
|
+
api_posts = @api.search_posts(tags: "api")
|
662
|
+
assert_equal 1, api_posts.length
|
663
|
+
assert_equal "Scriptorium API", api_posts.first.title
|
664
|
+
|
665
|
+
# Test multiple criteria (AND)
|
666
|
+
ruby_api_posts = @api.search_posts(title: /Ruby/, body: "basics")
|
667
|
+
assert_equal 1, ruby_api_posts.length
|
668
|
+
assert_equal "Ruby Programming", ruby_api_posts.first.title
|
669
|
+
end
|
670
|
+
|
671
|
+
def test_041_search_posts_with_blurb
|
672
|
+
@api.create_view("test_view", "Test View")
|
673
|
+
post = @api.create_post("Test Post", "Test body", blurb: "This is a test blurb for searching.")
|
674
|
+
|
675
|
+
# Test blurb search
|
676
|
+
blurb_posts = @api.search_posts(blurb: "test blurb")
|
677
|
+
assert_equal 1, blurb_posts.length
|
678
|
+
assert_equal "Test Post", blurb_posts.first.title
|
679
|
+
|
680
|
+
# Test blurb search with regex
|
681
|
+
regex_posts = @api.search_posts(blurb: /blurb.*search/)
|
682
|
+
assert_equal 1, regex_posts.length
|
683
|
+
assert_equal "Test Post", regex_posts.first.title
|
684
|
+
end
|
685
|
+
|
686
|
+
def test_042_search_posts_unknown_field
|
687
|
+
@api.create_view("test_view", "Test View")
|
688
|
+
|
689
|
+
# Create a post so the search actually processes something
|
690
|
+
@api.create_post("Test Post", "Test body")
|
691
|
+
|
692
|
+
assert_raises(UnknownSearchField) do
|
693
|
+
@api.search_posts(unknown_field: "value")
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
def test_043_unlink_post_specific_view
|
698
|
+
@api.create_view("test_view", "Test View")
|
699
|
+
@api.create_view("other_view", "Other View")
|
700
|
+
post = @api.create_post("Test Post", "Test body", views: ["test_view", "other_view"])
|
701
|
+
|
702
|
+
# Unlink from specific view
|
703
|
+
result = @api.unlink_post(post.id, "other_view")
|
704
|
+
assert result
|
705
|
+
|
706
|
+
# Post should now only be in test_view
|
707
|
+
updated_post = @api.post(post.id)
|
708
|
+
assert_includes updated_post.views, "test_view"
|
709
|
+
refute_includes updated_post.views, "other_view"
|
710
|
+
end
|
711
|
+
|
712
|
+
def test_044_post_add_tag
|
713
|
+
@api.create_view("test_view", "Test View")
|
714
|
+
post = @api.create_post("Test Post", "Test body", tags: ["ruby"])
|
715
|
+
|
716
|
+
# Initially post should only have ruby tag
|
717
|
+
assert_includes post.tags, "ruby"
|
718
|
+
refute_includes post.tags, "scriptorium"
|
719
|
+
|
720
|
+
# Add scriptorium tag to the post
|
721
|
+
result = @api.post_add_tag(post.id, "scriptorium")
|
722
|
+
assert result
|
723
|
+
|
724
|
+
# Post should now have both tags
|
725
|
+
updated_post = @api.post(post.id)
|
726
|
+
updated_tags = updated_post.tags.strip.split(/,\s*/)
|
727
|
+
assert_includes updated_tags, "ruby"
|
728
|
+
assert_includes updated_tags, "scriptorium"
|
729
|
+
end
|
730
|
+
|
731
|
+
def test_045_post_add_tag_duplicate
|
732
|
+
@api.create_view("test_view", "Test View")
|
733
|
+
post = @api.create_post("Test Post", "Test body", tags: ["ruby"])
|
734
|
+
|
735
|
+
# Initially post should have ruby tag
|
736
|
+
assert_includes post.tags, "ruby"
|
737
|
+
|
738
|
+
# Try to add the same tag (should not add duplicate)
|
739
|
+
result = @api.post_add_tag(post.id, "ruby")
|
740
|
+
assert result
|
741
|
+
|
742
|
+
# Post should still only have ruby tag (no duplicates)
|
743
|
+
updated_post = @api.post(post.id)
|
744
|
+
updated_tags = updated_post.tags.strip.split(/,\s*/)
|
745
|
+
assert_equal ["ruby"], updated_tags
|
746
|
+
end
|
747
|
+
|
748
|
+
def test_046_post_remove_tag
|
749
|
+
@api.create_view("test_view", "Test View")
|
750
|
+
post = @api.create_post("Test Post", "Test body", tags: ["ruby", "scriptorium"])
|
751
|
+
|
752
|
+
# Initially post should have both tags
|
753
|
+
assert_includes post.tags, "ruby"
|
754
|
+
assert_includes post.tags, "scriptorium"
|
755
|
+
|
756
|
+
# Remove scriptorium tag from the post
|
757
|
+
result = @api.post_remove_tag(post.id, "scriptorium")
|
758
|
+
assert result
|
759
|
+
|
760
|
+
# Post should now only have ruby tag
|
761
|
+
updated_post = @api.post(post.id)
|
762
|
+
updated_tags = updated_post.tags.strip.split(/,\s*/)
|
763
|
+
assert_includes updated_tags, "ruby"
|
764
|
+
refute_includes updated_tags, "scriptorium"
|
765
|
+
end
|
766
|
+
|
767
|
+
def test_047_post_remove_tag_nonexistent
|
768
|
+
@api.create_view("test_view", "Test View")
|
769
|
+
post = @api.create_post("Test Post", "Test body", tags: ["ruby"])
|
770
|
+
|
771
|
+
# Initially post should have ruby tag
|
772
|
+
assert_includes post.tags, "ruby"
|
773
|
+
|
774
|
+
# Try to remove a tag that doesn't exist
|
775
|
+
result = @api.post_remove_tag(post.id, "nonexistent")
|
776
|
+
assert result # Should succeed even if tag doesn't exist
|
777
|
+
|
778
|
+
# Post should still have ruby tag
|
779
|
+
updated_post = @api.post(post.id)
|
780
|
+
updated_tags = updated_post.tags.strip.split(/,\s*/)
|
781
|
+
assert_includes updated_tags, "ruby"
|
782
|
+
refute_includes updated_tags, "nonexistent"
|
783
|
+
end
|
784
|
+
|
785
|
+
# edit_file tests
|
786
|
+
def test_048_edit_file_validation_nil_path
|
787
|
+
assert_raises(EditFilePathNil) do
|
788
|
+
@api.edit_file(nil)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
def test_049_edit_file_validation_empty_path
|
793
|
+
assert_raises(EditFilePathEmpty) do
|
794
|
+
@api.edit_file("")
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
def test_050_edit_file_validation_whitespace_path
|
799
|
+
assert_raises(EditFilePathEmpty) do
|
800
|
+
@api.edit_file(" ")
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
def test_051_edit_file_uses_editor_from_env
|
805
|
+
# Mock ENV to return a specific editor
|
806
|
+
ENV.stub :[], "nano" do
|
807
|
+
# Mock system! to verify it's called with the right editor
|
808
|
+
mock_system = Minitest::Mock.new
|
809
|
+
mock_system.expect :call, true, ["nano", "/path/to/file"]
|
810
|
+
|
811
|
+
@api.stub :system!, mock_system do
|
812
|
+
@api.edit_file("/path/to/file")
|
813
|
+
end
|
814
|
+
|
815
|
+
mock_system.verify
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
819
|
+
def test_052_edit_file_uses_vim_fallback
|
820
|
+
# Mock ENV to return nil (no EDITOR set)
|
821
|
+
ENV.stub :[], nil do
|
822
|
+
# Mock system to return true (vim available)
|
823
|
+
@api.stub :system, true do
|
824
|
+
# Mock Open3.popen3 to return a mock process
|
825
|
+
mock_process = Minitest::Mock.new
|
826
|
+
mock_process.expect :pid, 123
|
827
|
+
mock_process.expect :wait, nil
|
828
|
+
|
829
|
+
Open3.stub :popen3, mock_process do
|
830
|
+
@api.edit_file("test.txt")
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
# Convenience file editing method tests
|
837
|
+
|
838
|
+
def test_053_edit_layout
|
839
|
+
@api.create_view("test_view", "Test View")
|
840
|
+
|
841
|
+
# Mock edit_file to track calls
|
842
|
+
called_path = nil
|
843
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
844
|
+
@api.edit_layout
|
845
|
+
assert_equal "views/test_view/layout.txt", called_path
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
def test_054_edit_layout_with_specific_view
|
850
|
+
@api.create_view("test_view", "Test View")
|
851
|
+
@api.create_view("other_view", "Other View")
|
852
|
+
|
853
|
+
# Mock edit_file to track calls
|
854
|
+
called_path = nil
|
855
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
856
|
+
@api.edit_layout("other_view")
|
857
|
+
assert_equal "views/other_view/layout.txt", called_path
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
def test_055_edit_layout_no_view
|
862
|
+
# Clear the current view
|
863
|
+
@api.repo.instance_variable_set(:@current_view, nil)
|
864
|
+
|
865
|
+
assert_raises(ViewTargetNil, "No view specified and no current view set") do
|
866
|
+
@api.edit_layout
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
def test_065_edit_config
|
871
|
+
@api.create_view("test_view", "Test View")
|
872
|
+
|
873
|
+
# Mock edit_file to track calls
|
874
|
+
called_path = nil
|
875
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
876
|
+
@api.edit_config
|
877
|
+
assert_equal "views/test_view/config.txt", called_path
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
def test_066_edit_config_with_specific_view
|
882
|
+
@api.create_view("test_view", "Test View")
|
883
|
+
@api.create_view("other_view", "Other View")
|
884
|
+
|
885
|
+
# Mock edit_file to track calls
|
886
|
+
called_path = nil
|
887
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
888
|
+
@api.edit_config("other_view")
|
889
|
+
assert_equal "views/other_view/config.txt", called_path
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
def test_067_edit_config_no_view
|
894
|
+
# Clear the current view
|
895
|
+
@api.repo.instance_variable_set(:@current_view, nil)
|
896
|
+
|
897
|
+
assert_raises(ViewTargetNil, "No view specified and no current view set") do
|
898
|
+
@api.edit_config
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
def test_056_edit_widget_data
|
903
|
+
@api.create_view("test_view", "Test View")
|
904
|
+
|
905
|
+
# Mock edit_file to track calls
|
906
|
+
called_path = nil
|
907
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
908
|
+
@api.edit_widget_data(nil, "links")
|
909
|
+
assert_equal "views/test_view/widgets/links/list.txt", called_path
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
def test_057_edit_widget_data_with_specific_view
|
914
|
+
@api.create_view("test_view", "Test View")
|
915
|
+
@api.create_view("other_view", "Other View")
|
916
|
+
|
917
|
+
# Mock edit_file to track calls
|
918
|
+
called_path = nil
|
919
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
920
|
+
@api.edit_widget_data("other_view", "news")
|
921
|
+
assert_equal "views/other_view/widgets/news/list.txt", called_path
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
def test_058_edit_widget_data_nil_widget
|
926
|
+
@api.create_view("test_view", "Test View")
|
927
|
+
|
928
|
+
assert_raises(WidgetNameNil, "Widget name cannot be nil") do
|
929
|
+
@api.edit_widget_data(nil, nil)
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
def test_060_edit_repo_config
|
934
|
+
# Mock edit_file to track calls
|
935
|
+
called_path = nil
|
936
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
937
|
+
@api.edit_repo_config
|
938
|
+
assert_equal "config/repo.txt", called_path
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
942
|
+
def test_061_edit_deploy_config
|
943
|
+
# Mock edit_file to track calls
|
944
|
+
called_path = nil
|
945
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
946
|
+
@api.edit_deploy_config
|
947
|
+
assert_equal "config/deploy.txt", called_path
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
951
|
+
def test_062_edit_post_with_source
|
952
|
+
@api.create_view("test_view", "Test View")
|
953
|
+
post = @api.create_post("Test Post", "Test body")
|
954
|
+
|
955
|
+
# Create source.lt3 file to test smart selection
|
956
|
+
source_path = "#{@test_dir}/posts/#{post.num}/source.lt3"
|
957
|
+
write_file(source_path, "Test source content")
|
958
|
+
|
959
|
+
# Mock edit_file to track calls
|
960
|
+
called_path = nil
|
961
|
+
@api.stub :edit_file, ->(path) { called_path = path } do
|
962
|
+
@api.edit_post(post.id)
|
963
|
+
assert_equal source_path, called_path
|
964
|
+
end
|
965
|
+
end
|
966
|
+
|
967
|
+
def test_063_edit_post_without_source
|
968
|
+
@api.create_view("test_view", "Test View")
|
969
|
+
post = @api.create_post("Test Post", "Test body")
|
970
|
+
|
971
|
+
# Ensure source.lt3 doesn't exist
|
972
|
+
source_path = "#{@test_dir}/posts/#{post.num}/source.lt3"
|
973
|
+
File.delete(source_path) if File.exist?(source_path)
|
974
|
+
|
975
|
+
# Should raise error since source.lt3 is required
|
976
|
+
assert_raises(RuntimeError) do
|
977
|
+
@api.edit_post(post.id)
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
def test_064_edit_post_nonexistent
|
982
|
+
assert_raises(CannotGetPost) do
|
983
|
+
@api.edit_post(999)
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
# Publication system tests
|
988
|
+
|
989
|
+
|
990
|
+
def test_068_publish_post
|
991
|
+
@api.create_view("test_view", "Test View")
|
992
|
+
post = @api.create_post("Test Post", "Test body")
|
993
|
+
|
994
|
+
# Initially unpublished
|
995
|
+
refute @api.post_published?(post.id)
|
996
|
+
|
997
|
+
# Publish the post
|
998
|
+
published_post = @api.publish_post(post.id)
|
999
|
+
|
1000
|
+
# Should now be published
|
1001
|
+
assert @api.post_published?(post.id)
|
1002
|
+
assert_equal "Test Post", published_post.title
|
1003
|
+
|
1004
|
+
# Should have generated the post
|
1005
|
+
assert File.exist?("#{@test_dir}/posts/#{post.num}/body.html")
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def test_069_publish_post_already_published
|
1009
|
+
@api.create_view("test_view", "Test View")
|
1010
|
+
|
1011
|
+
# Set test_view as current view
|
1012
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1013
|
+
|
1014
|
+
post = @api.create_post("Test Post", "Test body")
|
1015
|
+
|
1016
|
+
# Publish once
|
1017
|
+
@api.publish_post(post.id)
|
1018
|
+
|
1019
|
+
# Try to publish again
|
1020
|
+
assert_raises(PostAlreadyPublished, "Post #{post.id} is already published") do
|
1021
|
+
@api.publish_post(post.id)
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def test_070_publish_post_nonexistent
|
1026
|
+
assert_raises(CannotGetPost) do
|
1027
|
+
@api.publish_post(999)
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def test_070_5_unpublish_post
|
1032
|
+
@api.create_view("test_view", "Test View")
|
1033
|
+
post = @api.create_post("Test Post", "Test body")
|
1034
|
+
|
1035
|
+
# Publish the post
|
1036
|
+
@api.publish_post(post.id)
|
1037
|
+
assert @api.post_published?(post.id)
|
1038
|
+
|
1039
|
+
# Unpublish the post
|
1040
|
+
@api.unpublish_post(post.id)
|
1041
|
+
refute @api.post_published?(post.id)
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
def test_071_post_published_status
|
1045
|
+
@api.create_view("test_view", "Test View")
|
1046
|
+
post = @api.create_post("Test Post", "Test body")
|
1047
|
+
|
1048
|
+
# Initially unpublished
|
1049
|
+
refute @api.post_published?(post.id)
|
1050
|
+
|
1051
|
+
# Publish
|
1052
|
+
@api.publish_post(post.id)
|
1053
|
+
|
1054
|
+
# Now published
|
1055
|
+
assert @api.post_published?(post.id)
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def test_072_posts_with_published_parameter
|
1059
|
+
@api.create_view("test_view", "Test View")
|
1060
|
+
|
1061
|
+
# Create multiple posts
|
1062
|
+
post1 = @api.create_post("Post 1", "Body 1")
|
1063
|
+
post2 = @api.create_post("Post 2", "Body 2")
|
1064
|
+
post3 = @api.create_post("Post 3", "Body 3")
|
1065
|
+
|
1066
|
+
# Initially no published posts
|
1067
|
+
published_posts = @api.posts(published: true)
|
1068
|
+
assert_equal 0, published_posts.length
|
1069
|
+
|
1070
|
+
# Publish two posts
|
1071
|
+
@api.publish_post(post1.id)
|
1072
|
+
@api.publish_post(post3.id)
|
1073
|
+
|
1074
|
+
# Should have 2 published posts
|
1075
|
+
published_posts = @api.posts(published: true)
|
1076
|
+
assert_equal 2, published_posts.length
|
1077
|
+
assert_includes published_posts.map(&:id), post1.id
|
1078
|
+
assert_includes published_posts.map(&:id), post3.id
|
1079
|
+
refute_includes published_posts.map(&:id), post2.id
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
def test_073_posts_with_published_parameter_and_view
|
1083
|
+
@api.create_view("test_view", "Test View")
|
1084
|
+
@api.create_view("other_view", "Other View")
|
1085
|
+
|
1086
|
+
# Set test_view as current view
|
1087
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1088
|
+
|
1089
|
+
# Create posts in different views
|
1090
|
+
post1 = @api.create_post("Post 1", "Body 1", views: "test_view")
|
1091
|
+
post2 = @api.create_post("Post 2", "Body 2", views: "other_view")
|
1092
|
+
|
1093
|
+
# Publish post1 in test_view (current view)
|
1094
|
+
@api.publish_post(post1.id)
|
1095
|
+
|
1096
|
+
# Get published posts for specific view
|
1097
|
+
test_view_posts = @api.posts("test_view", published: true)
|
1098
|
+
assert_equal 1, test_view_posts.length
|
1099
|
+
assert_equal post1.id, test_view_posts.first.id
|
1100
|
+
|
1101
|
+
other_view_posts = @api.posts("other_view", published: true)
|
1102
|
+
assert_equal 0, other_view_posts.length # post2 not published
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def test_073_5_view_specific_publishing
|
1106
|
+
@api.create_view("test_view", "Test View")
|
1107
|
+
@api.create_view("other_view", "Other View")
|
1108
|
+
|
1109
|
+
# Set test_view as current view
|
1110
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1111
|
+
|
1112
|
+
# Create a post that belongs to both views
|
1113
|
+
post = @api.create_post("Test Post", "Test body", views: "test_view other_view")
|
1114
|
+
|
1115
|
+
# Initially unpublished in both views
|
1116
|
+
refute @api.post_published?(post.id, "test_view")
|
1117
|
+
refute @api.post_published?(post.id, "other_view")
|
1118
|
+
|
1119
|
+
# Publish in test_view only (current view)
|
1120
|
+
@api.publish_post(post.id)
|
1121
|
+
|
1122
|
+
# Should be published in test_view, unpublished in other_view
|
1123
|
+
assert @api.post_published?(post.id, "test_view")
|
1124
|
+
refute @api.post_published?(post.id, "other_view")
|
1125
|
+
|
1126
|
+
# Publish in other_view
|
1127
|
+
@api.publish_post(post.id, "other_view")
|
1128
|
+
|
1129
|
+
# Should be published in both views
|
1130
|
+
assert @api.post_published?(post.id, "test_view")
|
1131
|
+
assert @api.post_published?(post.id, "other_view")
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def test_073_6_deployment_state_management
|
1135
|
+
@api.create_view("test_view", "Test View")
|
1136
|
+
@api.create_view("other_view", "Other View")
|
1137
|
+
|
1138
|
+
# Set test_view as current view
|
1139
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1140
|
+
|
1141
|
+
# Create a post that belongs to both views
|
1142
|
+
post = @api.create_post("Test Post", "Test body", views: "test_view other_view")
|
1143
|
+
|
1144
|
+
# Initially unpublished and undeployed in both views
|
1145
|
+
refute @api.post_published?(post.id, "test_view")
|
1146
|
+
refute @api.post_deployed?(post.id, "test_view")
|
1147
|
+
refute @api.post_published?(post.id, "other_view")
|
1148
|
+
refute @api.post_deployed?(post.id, "other_view")
|
1149
|
+
|
1150
|
+
# Publish in test_view only
|
1151
|
+
@api.publish_post(post.id)
|
1152
|
+
|
1153
|
+
# Should be published but still undeployed in test_view
|
1154
|
+
assert @api.post_published?(post.id, "test_view")
|
1155
|
+
refute @api.post_deployed?(post.id, "test_view")
|
1156
|
+
|
1157
|
+
# Deploy in test_view
|
1158
|
+
@api.mark_post_deployed(post.id)
|
1159
|
+
|
1160
|
+
# Should be published and deployed in test_view
|
1161
|
+
assert @api.post_published?(post.id, "test_view")
|
1162
|
+
assert @api.post_deployed?(post.id, "test_view")
|
1163
|
+
|
1164
|
+
# Should still be unpublished and undeployed in other_view
|
1165
|
+
refute @api.post_published?(post.id, "other_view")
|
1166
|
+
refute @api.post_deployed?(post.id, "other_view")
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
def test_073_7_deployment_state_workflow
|
1170
|
+
@api.create_view("test_view", "Test View")
|
1171
|
+
|
1172
|
+
# Set test_view as current view
|
1173
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1174
|
+
|
1175
|
+
# Create multiple posts
|
1176
|
+
post1 = @api.create_post("Post 1", "Body 1")
|
1177
|
+
post2 = @api.create_post("Post 2", "Body 2")
|
1178
|
+
post3 = @api.create_post("Post 3", "Body 3")
|
1179
|
+
|
1180
|
+
# Initially all posts are unpublished and undeployed
|
1181
|
+
[post1, post2, post3].each do |post|
|
1182
|
+
refute @api.post_published?(post.id)
|
1183
|
+
refute @api.post_deployed?(post.id)
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
# Publish post1 and post3
|
1187
|
+
@api.publish_post(post1.id)
|
1188
|
+
@api.publish_post(post3.id)
|
1189
|
+
|
1190
|
+
# Deploy post1
|
1191
|
+
@api.mark_post_deployed(post1.id)
|
1192
|
+
|
1193
|
+
# Check states
|
1194
|
+
assert @api.post_published?(post1.id)
|
1195
|
+
assert @api.post_deployed?(post1.id)
|
1196
|
+
refute @api.post_published?(post2.id)
|
1197
|
+
refute @api.post_deployed?(post2.id)
|
1198
|
+
assert @api.post_published?(post3.id)
|
1199
|
+
refute @api.post_deployed?(post3.id)
|
1200
|
+
|
1201
|
+
# Get deployed posts
|
1202
|
+
deployed_posts = @api.get_deployed_posts
|
1203
|
+
assert_equal 1, deployed_posts.length
|
1204
|
+
assert_equal post1.id, deployed_posts.first.id
|
1205
|
+
|
1206
|
+
# Mark post1 as undeployed
|
1207
|
+
@api.mark_post_undeployed(post1.id)
|
1208
|
+
refute @api.post_deployed?(post1.id)
|
1209
|
+
|
1210
|
+
# Get deployed posts again
|
1211
|
+
deployed_posts = @api.get_deployed_posts
|
1212
|
+
assert_equal 0, deployed_posts.length
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
def test_073_8_deployment_workflow_integration
|
1216
|
+
@api.create_view("test_view", "Test View")
|
1217
|
+
|
1218
|
+
# Set test_view as current view
|
1219
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1220
|
+
|
1221
|
+
# Create a post (don't publish it yet)
|
1222
|
+
post = @api.create_post("Test Post", "Test body")
|
1223
|
+
|
1224
|
+
# Post should be unpublished and undeployed
|
1225
|
+
refute @api.post_published?(post.id)
|
1226
|
+
refute @api.post_deployed?(post.id)
|
1227
|
+
|
1228
|
+
# Try to deploy unpublished post (should fail)
|
1229
|
+
assert_raises(PostNotPublished) do
|
1230
|
+
@api.mark_post_deployed(post.id)
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
# Now publish the post
|
1234
|
+
@api.publish_post(post.id)
|
1235
|
+
|
1236
|
+
# Post should be published but not deployed
|
1237
|
+
assert @api.post_published?(post.id)
|
1238
|
+
refute @api.post_deployed?(post.id)
|
1239
|
+
|
1240
|
+
# Now deploy the published post
|
1241
|
+
@api.mark_post_deployed(post.id)
|
1242
|
+
|
1243
|
+
# Post should be both published and deployed
|
1244
|
+
assert @api.post_published?(post.id)
|
1245
|
+
assert @api.post_deployed?(post.id)
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
def test_073_9_post_states_display
|
1249
|
+
@api.create_view("test_view", "Test View")
|
1250
|
+
|
1251
|
+
# Set test_view as current view
|
1252
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1253
|
+
|
1254
|
+
# Create multiple posts
|
1255
|
+
post1 = @api.create_post("Post 1", "Body 1")
|
1256
|
+
post2 = @api.create_post("Post 2", "Body 2")
|
1257
|
+
post3 = @api.create_post("Post 3", "Body 3")
|
1258
|
+
|
1259
|
+
# Get initial states
|
1260
|
+
states = @api.get_post_states
|
1261
|
+
assert_equal 3, states.length
|
1262
|
+
|
1263
|
+
# Check initial state (should be "-" for unpublished/undeployed)
|
1264
|
+
assert_equal "-", states[post1.id][:state]
|
1265
|
+
assert_equal "-", states[post2.id][:state]
|
1266
|
+
assert_equal "-", states[post3.id][:state]
|
1267
|
+
|
1268
|
+
# Publish post1
|
1269
|
+
@api.publish_post(post1.id)
|
1270
|
+
states = @api.get_post_states
|
1271
|
+
assert_equal "P", states[post1.id][:state] # Published only
|
1272
|
+
|
1273
|
+
# Deploy post1
|
1274
|
+
@api.mark_post_deployed(post1.id)
|
1275
|
+
states = @api.get_post_states
|
1276
|
+
assert_equal "PD", states[post1.id][:state] # Published and deployed
|
1277
|
+
|
1278
|
+
# Publish post3
|
1279
|
+
@api.publish_post(post3.id)
|
1280
|
+
states = @api.get_post_states
|
1281
|
+
assert_equal "PD", states[post1.id][:state] # Published and deployed
|
1282
|
+
assert_equal "P", states[post3.id][:state] # Published only
|
1283
|
+
assert_equal "-", states[post2.id][:state] # Neither
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
def test_073_10_state_validation_rules
|
1287
|
+
@api.create_view("test_view", "Test View")
|
1288
|
+
|
1289
|
+
# Set test_view as current view
|
1290
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1291
|
+
|
1292
|
+
# Create a post
|
1293
|
+
post = @api.create_post("Test Post", "Test body")
|
1294
|
+
|
1295
|
+
# Test: Cannot publish deleted post
|
1296
|
+
@api.delete_post(post.id)
|
1297
|
+
assert_raises(PostDeleted) do
|
1298
|
+
@api.publish_post(post.id)
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
# Test: Cannot deploy deleted post
|
1302
|
+
assert_raises(PostDeleted) do
|
1303
|
+
@api.mark_post_deployed(post.id)
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
# Test: Cannot unpublish deployed post
|
1307
|
+
@api.undelete_post(post.id)
|
1308
|
+
@api.publish_post(post.id)
|
1309
|
+
@api.mark_post_deployed(post.id)
|
1310
|
+
assert_raises(PostAlreadyDeployed) do
|
1311
|
+
@api.unpublish_post(post.id)
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
def test_073_11_delete_undelete_workflow
|
1316
|
+
@api.create_view("test_view", "Test View")
|
1317
|
+
|
1318
|
+
# Set test_view as current view
|
1319
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1320
|
+
|
1321
|
+
# Create a post
|
1322
|
+
post = @api.create_post("Test Post", "Test body")
|
1323
|
+
|
1324
|
+
# Post should not be deleted initially
|
1325
|
+
refute @api.post_deleted?(post.id)
|
1326
|
+
|
1327
|
+
# Delete the post
|
1328
|
+
@api.delete_post(post.id)
|
1329
|
+
assert @api.post_deleted?(post.id)
|
1330
|
+
|
1331
|
+
# Post should be in deleted state (X)
|
1332
|
+
states = @api.get_post_states
|
1333
|
+
assert_equal "X", states[post.id][:state]
|
1334
|
+
|
1335
|
+
# Undelete the post
|
1336
|
+
@api.undelete_post(post.id)
|
1337
|
+
refute @api.post_deleted?(post.id)
|
1338
|
+
|
1339
|
+
# Post should be back to normal state (-)
|
1340
|
+
states = @api.get_post_states
|
1341
|
+
assert_equal "-", states[post.id][:state]
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
def test_073_12_edit_state_transition
|
1345
|
+
@api.create_view("test_view", "Test View")
|
1346
|
+
|
1347
|
+
# Set test_view as current view
|
1348
|
+
@api.repo.instance_variable_set(:@current_view, @api.repo.lookup_view("test_view"))
|
1349
|
+
|
1350
|
+
# Create and publish a post
|
1351
|
+
post = @api.create_post("Test Post", "Test body")
|
1352
|
+
@api.publish_post(post.id)
|
1353
|
+
@api.mark_post_deployed(post.id)
|
1354
|
+
|
1355
|
+
# Post should be published and deployed
|
1356
|
+
assert @api.post_published?(post.id)
|
1357
|
+
assert @api.post_deployed?(post.id)
|
1358
|
+
|
1359
|
+
# Regenerate the post - should reset state to unpublished/undeployed
|
1360
|
+
@api.repo.generate_post(post.id)
|
1361
|
+
|
1362
|
+
# Post should now be unpublished and undeployed (content changed)
|
1363
|
+
refute @api.post_published?(post.id)
|
1364
|
+
refute @api.post_deployed?(post.id)
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
def test_074_create_post_with_generation
|
1368
|
+
@api.create_view("test_view", "Test View")
|
1369
|
+
post = @api.create_post("Test Post", "Test body")
|
1370
|
+
|
1371
|
+
# Post should exist and be generated by default
|
1372
|
+
assert File.exist?("#{@test_dir}/posts/#{post.num}/source.lt3")
|
1373
|
+
assert File.exist?("#{@test_dir}/posts/#{post.num}/meta.txt")
|
1374
|
+
assert File.exist?("#{@test_dir}/posts/#{post.num}/body.html")
|
1375
|
+
|
1376
|
+
# Should not be published
|
1377
|
+
refute @api.post_published?(post.id)
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def test_075_create_post_with_generation_default
|
1381
|
+
@api.create_view("test_view", "Test View")
|
1382
|
+
post = @api.create_post("Test Post", "Test body")
|
1383
|
+
|
1384
|
+
# Post should be generated by default (backward compatibility)
|
1385
|
+
assert File.exist?("#{@test_dir}/posts/#{post.num}/body.html")
|
1386
|
+
|
1387
|
+
# Should NOT be published (generation and publication are separate)
|
1388
|
+
# Check the metadata file directly to be sure
|
1389
|
+
metadata_file = "#{@test_dir}/posts/#{post.num}/meta.txt"
|
1390
|
+
assert File.exist?(metadata_file)
|
1391
|
+
metadata_content = read_file(metadata_file)
|
1392
|
+
assert_match /post\.published\s+no/, metadata_content
|
1393
|
+
refute @api.post_published?(post.id)
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def test_999_placeholder
|
1397
|
+
# This test ensures the file has at least one test method
|
1398
|
+
assert true
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
# Asset management tests
|
1402
|
+
|
1403
|
+
def test_1000_list_assets_global
|
1404
|
+
# @api is already set up in setup method
|
1405
|
+
|
1406
|
+
# Create some test assets
|
1407
|
+
write_file("test/scriptorium-TEST/assets/test1.jpg", "Test image 1")
|
1408
|
+
write_file("test/scriptorium-TEST/assets/test2.png", "Test image 2")
|
1409
|
+
|
1410
|
+
assets = @api.list_assets(target: 'global')
|
1411
|
+
|
1412
|
+
assert_equal 3, assets.length
|
1413
|
+
# Check that our test assets are present (imagenotfound.jpg will also be there)
|
1414
|
+
filenames = assets.map { |a| a[:filename] }
|
1415
|
+
assert_includes filenames, "test1.jpg"
|
1416
|
+
assert_includes filenames, "test2.png"
|
1417
|
+
assert_includes filenames, "imagenotfound.jpg"
|
1418
|
+
assert_equal "image", assets[0][:type]
|
1419
|
+
assert assets[0][:size] > 0
|
1420
|
+
end
|
1421
|
+
|
1422
|
+
def test_1001_list_assets_library
|
1423
|
+
# @api is already set up in setup method
|
1424
|
+
|
1425
|
+
# Create library assets
|
1426
|
+
write_file("test/scriptorium-TEST/assets/library/sample1.jpg", "Sample 1")
|
1427
|
+
write_file("test/scriptorium-TEST/assets/library/sample2.txt", "Sample 2")
|
1428
|
+
|
1429
|
+
assets = @api.list_assets(target: 'library')
|
1430
|
+
|
1431
|
+
# There might be existing assets from setup, so check that our test assets are included
|
1432
|
+
assert assets.length >= 2, "Should have at least our 2 test assets"
|
1433
|
+
filenames = assets.map { |a| a[:filename] }
|
1434
|
+
assert_includes filenames, "sample1.jpg"
|
1435
|
+
assert_includes filenames, "sample2.txt"
|
1436
|
+
|
1437
|
+
# Find our specific test assets
|
1438
|
+
sample1 = assets.find { |a| a[:filename] == "sample1.jpg" }
|
1439
|
+
sample2 = assets.find { |a| a[:filename] == "sample2.txt" }
|
1440
|
+
|
1441
|
+
assert_equal "image", sample1[:type]
|
1442
|
+
assert_equal "document", sample2[:type]
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
def test_1002_list_assets_view
|
1446
|
+
# @api is already set up in setup method
|
1447
|
+
@api.create_view("testview", "Test View", "Test Subtitle")
|
1448
|
+
|
1449
|
+
# Create view assets
|
1450
|
+
write_file("test/scriptorium-TEST/views/testview/assets/view1.jpg", "View asset 1")
|
1451
|
+
write_file("test/scriptorium-TEST/views/testview/assets/view2.svg", "View asset 2")
|
1452
|
+
|
1453
|
+
assets = @api.list_assets(target: 'view', view: 'testview')
|
1454
|
+
|
1455
|
+
assert_equal 3, assets.length
|
1456
|
+
# Check that our test assets are present (imagenotfound.jpg will also be there)
|
1457
|
+
filenames = assets.map { |a| a[:filename] }
|
1458
|
+
assert_includes filenames, "view1.jpg"
|
1459
|
+
assert_includes filenames, "view2.svg"
|
1460
|
+
assert_includes filenames, "imagenotfound.jpg"
|
1461
|
+
assert_equal "image", assets[0][:type]
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
def test_1003_list_assets_gem
|
1465
|
+
# @api is already set up in setup method
|
1466
|
+
|
1467
|
+
# Test gem assets (should work in development environment)
|
1468
|
+
assets = @api.list_assets(target: 'gem')
|
1469
|
+
|
1470
|
+
# In development environment, we should find assets from the working directory
|
1471
|
+
# If no gem assets are found, that's also acceptable
|
1472
|
+
if assets.length > 0
|
1473
|
+
assert assets.all? { |asset| asset[:type] == 'image' || asset[:type] == 'other' }
|
1474
|
+
end
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
def test_1004_get_asset_info
|
1478
|
+
# @api is already set up in setup method
|
1479
|
+
|
1480
|
+
# Create test asset
|
1481
|
+
write_file("test/scriptorium-TEST/assets/test_info.jpg", "Test info image")
|
1482
|
+
|
1483
|
+
asset_info = @api.get_asset_info("test_info.jpg", target: 'global')
|
1484
|
+
|
1485
|
+
assert_equal "test_info.jpg", asset_info[:filename]
|
1486
|
+
assert_equal "image", asset_info[:type]
|
1487
|
+
assert asset_info[:size] > 0
|
1488
|
+
assert asset_info[:path].include?("test_info.jpg")
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
def test_1005_asset_exists
|
1492
|
+
# @api is already set up in setup method
|
1493
|
+
|
1494
|
+
# Create test asset
|
1495
|
+
write_file("test/scriptorium-TEST/assets/exists.jpg", "Exists")
|
1496
|
+
|
1497
|
+
assert @api.asset_exists?("exists.jpg", target: 'global')
|
1498
|
+
refute @api.asset_exists?("missing.jpg", target: 'global')
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
def test_1006_copy_asset_global_to_view
|
1502
|
+
# @api is already set up in setup method
|
1503
|
+
@api.create_view("testview", "Test View", "Test Subtitle")
|
1504
|
+
|
1505
|
+
# Create source asset
|
1506
|
+
write_file("test/scriptorium-TEST/assets/source.jpg", "Source image")
|
1507
|
+
|
1508
|
+
# Copy asset
|
1509
|
+
target_path = @api.copy_asset("source.jpg", from: 'global', to: 'view', view: 'testview')
|
1510
|
+
|
1511
|
+
# Verify copy
|
1512
|
+
assert File.exist?(target_path)
|
1513
|
+
assert File.exist?("test/scriptorium-TEST/views/testview/assets/source.jpg")
|
1514
|
+
assert_equal "Source image", read_file("test/scriptorium-TEST/views/testview/assets/source.jpg").chomp
|
1515
|
+
end
|
1516
|
+
|
1517
|
+
def test_1007_copy_asset_gem_to_global
|
1518
|
+
# @api is already set up in setup method
|
1519
|
+
|
1520
|
+
# Find a gem asset to copy
|
1521
|
+
gem_assets = @api.list_assets(target: 'gem')
|
1522
|
+
skip "No gem assets available for testing" if gem_assets.empty?
|
1523
|
+
|
1524
|
+
gem_filename = gem_assets.first[:filename]
|
1525
|
+
|
1526
|
+
# Copy from gem to global
|
1527
|
+
target_path = @api.copy_asset(gem_filename, from: 'gem', to: 'global')
|
1528
|
+
|
1529
|
+
# Verify copy
|
1530
|
+
assert File.exist?(target_path)
|
1531
|
+
assert File.exist?("test/scriptorium-TEST/assets/#{gem_filename}")
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
def test_1008_copy_asset_library_to_view
|
1535
|
+
# @api is already set up in setup method
|
1536
|
+
@api.create_view("testview", "Test View", "Test Subtitle")
|
1537
|
+
|
1538
|
+
# Create library asset
|
1539
|
+
write_file("test/scriptorium-TEST/assets/library/lib.jpg", "Library image")
|
1540
|
+
|
1541
|
+
# Copy to view
|
1542
|
+
target_path = @api.copy_asset("lib.jpg", from: 'library', to: 'view', view: 'testview')
|
1543
|
+
|
1544
|
+
# Verify copy
|
1545
|
+
assert File.exist?(target_path)
|
1546
|
+
assert File.exist?("test/scriptorium-TEST/views/testview/assets/lib.jpg")
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
def test_1009_upload_asset
|
1550
|
+
# @api is already set up in setup method
|
1551
|
+
|
1552
|
+
# Create temporary source file
|
1553
|
+
source_file = "test/temp_source.jpg"
|
1554
|
+
write_file(source_file, "Temporary source image")
|
1555
|
+
|
1556
|
+
# Upload to global
|
1557
|
+
target_path = @api.upload_asset(source_file, target: 'global')
|
1558
|
+
|
1559
|
+
# Verify upload
|
1560
|
+
assert File.exist?(target_path)
|
1561
|
+
assert File.exist?("test/scriptorium-TEST/assets/temp_source.jpg")
|
1562
|
+
assert_equal "Temporary source image", read_file("test/scriptorium-TEST/assets/temp_source.jpg").chomp
|
1563
|
+
|
1564
|
+
# Cleanup
|
1565
|
+
File.delete(source_file)
|
1566
|
+
end
|
1567
|
+
|
1568
|
+
def test_1010_upload_asset_to_view
|
1569
|
+
# @api is already set up in setup method
|
1570
|
+
@api.create_view("testview", "Test View", "Test Subtitle")
|
1571
|
+
|
1572
|
+
# Create temporary source file
|
1573
|
+
source_file = "test/temp_view_source.jpg"
|
1574
|
+
write_file(source_file, "View source image")
|
1575
|
+
|
1576
|
+
# Upload to view
|
1577
|
+
target_path = @api.upload_asset(source_file, target: 'view', view: 'testview')
|
1578
|
+
|
1579
|
+
# Verify upload
|
1580
|
+
assert File.exist?(target_path)
|
1581
|
+
assert File.exist?("test/scriptorium-TEST/views/testview/assets/temp_view_source.jpg")
|
1582
|
+
|
1583
|
+
# Cleanup
|
1584
|
+
File.delete(source_file)
|
1585
|
+
end
|
1586
|
+
|
1587
|
+
def test_1011_delete_asset
|
1588
|
+
# @api is already set up in setup method
|
1589
|
+
|
1590
|
+
# Create test asset
|
1591
|
+
write_file("test/scriptorium-TEST/assets/to_delete.jpg", "Delete me")
|
1592
|
+
|
1593
|
+
# Verify it exists
|
1594
|
+
assert File.exist?("test/scriptorium-TEST/assets/to_delete.jpg")
|
1595
|
+
|
1596
|
+
# Delete it
|
1597
|
+
result = @api.delete_asset("to_delete.jpg", target: 'global')
|
1598
|
+
|
1599
|
+
# Verify deletion
|
1600
|
+
assert result
|
1601
|
+
refute File.exist?("test/scriptorium-TEST/assets/to_delete.jpg")
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
def test_1012_get_asset_path
|
1605
|
+
# @api is already set up in setup method
|
1606
|
+
|
1607
|
+
# Create test asset
|
1608
|
+
write_file("test/scriptorium-TEST/assets/path_test.jpg", "Path test")
|
1609
|
+
|
1610
|
+
# Get path
|
1611
|
+
path = @api.get_asset_path("path_test.jpg", target: 'global')
|
1612
|
+
|
1613
|
+
assert path.include?("path_test.jpg")
|
1614
|
+
assert path.include?("assets")
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
def test_1013_get_asset_dimensions
|
1618
|
+
# @api is already set up in setup method
|
1619
|
+
|
1620
|
+
# Create test image (we'll use a placeholder)
|
1621
|
+
write_file("test/scriptorium-TEST/assets/dimensions.jpg", "Image data")
|
1622
|
+
|
1623
|
+
# Get dimensions (may be nil if FastImage not available)
|
1624
|
+
dimensions = @api.get_asset_dimensions("dimensions.jpg", target: 'global')
|
1625
|
+
|
1626
|
+
# Just verify the method doesn't crash
|
1627
|
+
assert true
|
1628
|
+
end
|
1629
|
+
|
1630
|
+
def test_1014_get_asset_size
|
1631
|
+
# @api is already set up in setup method
|
1632
|
+
|
1633
|
+
# Create test asset
|
1634
|
+
content = "Test content for size measurement"
|
1635
|
+
write_file("test/scriptorium-TEST/assets/size_test.txt", content)
|
1636
|
+
|
1637
|
+
# Get size (write_file adds a newline, so size will be content.length + 1)
|
1638
|
+
size = @api.get_asset_size("size_test.txt", target: 'global')
|
1639
|
+
|
1640
|
+
assert_equal content.length + 1, size
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
def test_1015_get_asset_type
|
1644
|
+
# @api is already set up in setup method
|
1645
|
+
|
1646
|
+
# Test various file types
|
1647
|
+
assert_equal "image", @api.get_asset_type("test.jpg")
|
1648
|
+
assert_equal "image", @api.get_asset_type("test.png")
|
1649
|
+
assert_equal "image", @api.get_asset_type("test.svg")
|
1650
|
+
assert_equal "document", @api.get_asset_type("test.txt")
|
1651
|
+
assert_equal "document", @api.get_asset_type("test.md")
|
1652
|
+
assert_equal "video", @api.get_asset_type("test.mp4")
|
1653
|
+
assert_equal "audio", @api.get_asset_type("test.mp3")
|
1654
|
+
assert_equal "other", @api.get_asset_type("test.xyz")
|
1655
|
+
assert_nil @api.get_asset_type(nil)
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
def test_1016_bulk_copy_assets
|
1659
|
+
# @api is already set up in setup method
|
1660
|
+
@api.create_view("testview", "Test View", "Test Subtitle")
|
1661
|
+
|
1662
|
+
# Create multiple source assets
|
1663
|
+
write_file("test/scriptorium-TEST/assets/bulk1.jpg", "Bulk 1")
|
1664
|
+
write_file("test/scriptorium-TEST/assets/bulk2.png", "Bulk 2")
|
1665
|
+
write_file("test/scriptorium-TEST/assets/bulk3.txt", "Bulk 3")
|
1666
|
+
|
1667
|
+
filenames = ["bulk1.jpg", "bulk2.png", "bulk3.txt"]
|
1668
|
+
|
1669
|
+
# Bulk copy
|
1670
|
+
results = @api.bulk_copy_assets(filenames, from: 'global', to: 'view', view: 'testview')
|
1671
|
+
|
1672
|
+
# Verify results
|
1673
|
+
assert_equal 3, results.length
|
1674
|
+
assert results.all? { |r| r[:success] }
|
1675
|
+
|
1676
|
+
# Verify files were copied
|
1677
|
+
filenames.each do |filename|
|
1678
|
+
assert File.exist?("test/scriptorium-TEST/views/testview/assets/#{filename}")
|
1679
|
+
end
|
1680
|
+
end
|
1681
|
+
|
1682
|
+
def test_1017_copy_asset_invalid_source
|
1683
|
+
# @api is already set up in setup method
|
1684
|
+
|
1685
|
+
# Try to copy from invalid source
|
1686
|
+
assert_raises(InvalidFormatError) do
|
1687
|
+
@api.copy_asset("test.jpg", from: 'invalid', to: 'global')
|
1688
|
+
end
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
def test_1018_copy_asset_invalid_target
|
1692
|
+
# @api is already set up in setup method
|
1693
|
+
|
1694
|
+
# Try to copy to invalid target
|
1695
|
+
assert_raises(InvalidFormatError) do
|
1696
|
+
@api.copy_asset("test.jpg", from: 'global', to: 'invalid')
|
1697
|
+
end
|
1698
|
+
end
|
1699
|
+
|
1700
|
+
def test_1019_copy_asset_source_not_found
|
1701
|
+
# @api is already set up in setup method
|
1702
|
+
|
1703
|
+
# Try to copy non-existent asset
|
1704
|
+
assert_raises(FileNotFoundError) do
|
1705
|
+
@api.copy_asset("missing.jpg", from: 'global', to: 'global')
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
|
1709
|
+
def test_1020_list_assets_no_view_specified
|
1710
|
+
# @api is already set up in setup method
|
1711
|
+
|
1712
|
+
# Should work for global assets
|
1713
|
+
assets = @api.list_assets(target: 'global')
|
1714
|
+
assert assets.is_a?(Array)
|
1715
|
+
|
1716
|
+
# Clear the current view to test the error case
|
1717
|
+
@api.repo.instance_variable_set(:@current_view, nil)
|
1718
|
+
|
1719
|
+
# Should fail for view assets without view
|
1720
|
+
assert_raises(ViewTargetNil) do
|
1721
|
+
@api.list_assets(target: 'view')
|
1722
|
+
end
|
1723
|
+
end
|
1724
|
+
|
1725
|
+
def test_999_creates_posts_with_sequential_ids
|
1726
|
+
# Create three posts
|
1727
|
+
post1 = @api.create_post(
|
1728
|
+
"First Post",
|
1729
|
+
"This is the first post content.",
|
1730
|
+
views: "sample",
|
1731
|
+
tags: "test"
|
1732
|
+
)
|
1733
|
+
|
1734
|
+
post2 = @api.create_post(
|
1735
|
+
"Second Post",
|
1736
|
+
"This is the second post content.",
|
1737
|
+
views: "sample",
|
1738
|
+
tags: "test"
|
1739
|
+
)
|
1740
|
+
|
1741
|
+
post3 = @api.create_post(
|
1742
|
+
"Third Post",
|
1743
|
+
"This is the third post content.",
|
1744
|
+
views: "sample",
|
1745
|
+
tags: "test"
|
1746
|
+
)
|
1747
|
+
|
1748
|
+
# Verify they have sequential IDs
|
1749
|
+
assert_equal 1, post1.id, "First post should have ID 1"
|
1750
|
+
assert_equal 2, post2.id, "Second post should have ID 2"
|
1751
|
+
assert_equal 3, post3.id, "Third post should have ID 3"
|
1752
|
+
|
1753
|
+
# Verify the posts exist in the repository
|
1754
|
+
assert_equal "First Post", @api.post(1).title
|
1755
|
+
assert_equal "Second Post", @api.post(2).title
|
1756
|
+
assert_equal "Third Post", @api.post(3).title
|
1757
|
+
end
|
1758
|
+
|
1759
|
+
def test_998_post_counter_is_correctly_updated
|
1760
|
+
# Check initial counter
|
1761
|
+
initial_counter = @api.instance_variable_get(:@repo).last_post_num
|
1762
|
+
assert_equal 0, initial_counter, "Initial counter should be 0"
|
1763
|
+
|
1764
|
+
# Create a post
|
1765
|
+
post = @api.create_post(
|
1766
|
+
"Test Post",
|
1767
|
+
"Test content",
|
1768
|
+
views: "sample"
|
1769
|
+
)
|
1770
|
+
|
1771
|
+
# Check counter after creation
|
1772
|
+
updated_counter = @api.instance_variable_get(:@repo).last_post_num
|
1773
|
+
assert_equal 1, updated_counter, "Counter should be 1 after creating one post"
|
1774
|
+
assert_equal 1, post.id, "Post should have ID 1"
|
1775
|
+
end
|
1776
|
+
end
|