From e643e20b7211d717d6cdda50b3d3e77b3912cd9b Mon Sep 17 00:00:00 2001 From: Nathan Richards Date: Thu, 13 Aug 2026 15:41:11 +0200 Subject: [PATCH 1/4] test: seed a public Scratch project for preview Add an unowned Blocks template to test seeds so local and preview work can load Scratch content without auth. --- lib/tasks/seed_data/excs_preview_starter.json | 100 ++++++++++++++++++ .../excs_preview_starter_instructions.json | 11 ++ lib/tasks/seeds_helper.rb | 39 +++++++ lib/tasks/test_seeds.rake | 4 + spec/lib/test_seeds_spec.rb | 53 ++++++++++ 5 files changed, 207 insertions(+) create mode 100644 lib/tasks/seed_data/excs_preview_starter.json create mode 100644 lib/tasks/seed_data/excs_preview_starter_instructions.json diff --git a/lib/tasks/seed_data/excs_preview_starter.json b/lib/tasks/seed_data/excs_preview_starter.json new file mode 100644 index 000000000..298a4f11b --- /dev/null +++ b/lib/tasks/seed_data/excs_preview_starter.json @@ -0,0 +1,100 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "variables": { + "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] + }, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "backdrop1", + "dataFormat": "svg", + "assetId": "cd21514d0531fdffb22204e0ec5ed84a", + "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "dataFormat": "wav", + "format": "", + "rate": 48000, + "sampleCount": 1123, + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Sprite1", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "costume1", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 48, + "rotationCenterY": 50 + }, + { + "name": "costume2", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "0fb9be3e8397c983338cb71dc84d0b25", + "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", + "rotationCenterX": 46, + "rotationCenterY": 53 + } + ], + "sounds": [ + { + "name": "Meow", + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "dataFormat": "wav", + "format": "", + "rate": 48000, + "sampleCount": 40681, + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 1, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "12.7.0", + "agent": "default" + } +} diff --git a/lib/tasks/seed_data/excs_preview_starter_instructions.json b/lib/tasks/seed_data/excs_preview_starter_instructions.json new file mode 100644 index 000000000..bf3fc380e --- /dev/null +++ b/lib/tasks/seed_data/excs_preview_starter_instructions.json @@ -0,0 +1,11 @@ +[ + { + "markdown_content": "## Welcome\n\nThis is a sample Experience CS Blocks project you can try without signing in." + }, + { + "markdown_content": "## Try it out\n\n1. Click the green flag to run the project.\n2. Explore the sprites and backdrops.\n3. Change a block and run it again." + }, + { + "markdown_content": "## What's next?\n\nWhen you use this project in a class, teachers can add their own steps here for learners." + } +] diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index 5ee229192..7b697555e 100644 --- a/lib/tasks/seeds_helper.rb +++ b/lib/tasks/seeds_helper.rb @@ -14,6 +14,13 @@ module SeedsHelper TEST_SCHOOL = 'e52de409-9210-4e94-b08c-dd11439e07d9' # e52de409-9210-4e94-b08c-dd11439e07d9 SCHOOL_CODE = '12-34-56' + # Public Blocks template for Experience CS project preview (unowned, loadable without auth). + PROJECT_PREVIEW_IDENTIFIER = 'excs-preview-starter' + PROJECT_PREVIEW_LOCALE = 'en' + PROJECT_PREVIEW_NAME = 'Experience CS Preview Starter' + PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter.json') + PROJECT_PREVIEW_INSTRUCTIONS_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions.json') + def create_school(creator_id, school_id = nil) School.find_or_create_by!(creator_id:, id: school_id) do |school| Rails.logger.info 'Seeding a school...' @@ -115,4 +122,36 @@ def create_project(user_id, school, lesson, code = '') content: code }) end end + + def create_public_scratch_preview_project + project = Project.find_or_initialize_by( + identifier: PROJECT_PREVIEW_IDENTIFIER, + locale: PROJECT_PREVIEW_LOCALE + ) + Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}'..." + project.name = PROJECT_PREVIEW_NAME + project.user_id = nil + project.school = nil + project.project_type = Project::Types::CODE_EDITOR_SCRATCH + project.instructions = public_scratch_preview_instructions + if project.scratch_component + project.scratch_component.content = public_scratch_preview_content + else + project.scratch_component = ScratchComponent.new(content: public_scratch_preview_content) + end + project.save! + project + end + + def destroy_public_scratch_preview_project + Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER, locale: PROJECT_PREVIEW_LOCALE).destroy_all + end + + def public_scratch_preview_content + JSON.parse(File.read(PROJECT_PREVIEW_CONTENT_PATH)) + end + + def public_scratch_preview_instructions + JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_PATH)) + end end diff --git a/lib/tasks/test_seeds.rake b/lib/tasks/test_seeds.rake index 9a7bbcc2b..f3183b862 100644 --- a/lib/tasks/test_seeds.rake +++ b/lib/tasks/test_seeds.rake @@ -37,6 +37,8 @@ namespace :test_seeds do school_ids = [school_id, teacher_signup_school_id].compact School.where(id: school_ids).destroy_all + destroy_public_scratch_preview_project + # Clear any feature flags that may have been set Flipper.features.each(&:remove) @@ -46,6 +48,8 @@ namespace :test_seeds do desc 'Create a school with lessons and students' task create: :environment do + create_public_scratch_preview_project + if School.exists?(id: TEST_SCHOOL) puts "Test school (#{TEST_SCHOOL}) already exists, run the destroy_seed_data task to start over)." else diff --git a/spec/lib/test_seeds_spec.rb b/spec/lib/test_seeds_spec.rb index 8c88d54d1..fc6f9ac1f 100644 --- a/spec/lib/test_seeds_spec.rb +++ b/spec/lib/test_seeds_spec.rb @@ -39,6 +39,25 @@ expect(ScratchAsset.where(project_id: scratch_project_id)).not_to exist end + it 'destroys the public Scratch preview project' do + create( + :scratch_project, + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE, + user_id: nil, + name: SeedsHelper::PROJECT_PREVIEW_NAME + ) + + task.invoke + + expect( + Project.where( + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE + ) + ).not_to exist + end + it 'removes all feature flags' do Flipper.enable(:some_feature) task.invoke @@ -60,6 +79,40 @@ expect(School.find_by(creator_id:).verified_at).to be_truthy end + it 'creates a public Scratch preview project' do + project = Project.find_by!( + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE + ) + + expect(project).to have_attributes( + name: SeedsHelper::PROJECT_PREVIEW_NAME, + user_id: nil, + school_id: nil, + project_type: Project::Types::CODE_EDITOR_SCRATCH + ) + expect(project.scratch_component).to be_present + expect(project.scratch_component.content.to_h).to include('targets') + expect(project.instructions).to eq( + JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_PATH)) + ) + end + + it 'updates instructions on an existing public Scratch preview project' do + project = Project.find_by!( + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE + ) + project.update!(instructions: [{ markdown_content: 'stale step' }]) + + task.reenable + task.invoke + + expect(project.reload.instructions).to eq( + JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_PATH)) + ) + end + it 'creates lessons with projects' do school = School.find_by(creator_id:) expect(SchoolClass.where(school_id: school.id)).to exist From 99c5169b7e08c00d4afaae4c59f75b6e7fea4a60 Mon Sep 17 00:00:00 2001 From: Nathan Richards Date: Thu, 13 Aug 2026 19:20:24 +0200 Subject: [PATCH 2/4] test: seed a French locale ExCS preview project Add an fr-FR row for excs-preview-starter so locale-aware preview loads can return translated name and instructions instead of falling back to English. --- .../excs_preview_starter_instructions_fr.json | 11 +++++++ lib/tasks/seeds_helper.rb | 32 ++++++++++++++++--- spec/lib/test_seeds_spec.rb | 30 ++++++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 lib/tasks/seed_data/excs_preview_starter_instructions_fr.json diff --git a/lib/tasks/seed_data/excs_preview_starter_instructions_fr.json b/lib/tasks/seed_data/excs_preview_starter_instructions_fr.json new file mode 100644 index 000000000..b4f061236 --- /dev/null +++ b/lib/tasks/seed_data/excs_preview_starter_instructions_fr.json @@ -0,0 +1,11 @@ +[ + { + "markdown_content": "## Bienvenue\n\nVoici un projet Blocks Experience CS d'exemple que vous pouvez essayer sans vous connecter." + }, + { + "markdown_content": "## Essayez\n\n1. Cliquez sur le drapeau vert pour lancer le projet.\n2. Explorez les lutins et les arrière-plans.\n3. Modifiez un bloc et relancez." + }, + { + "markdown_content": "## Et ensuite ?\n\nLorsque vous utilisez ce projet en classe, les enseignants peuvent ajouter ici leurs propres étapes pour les élèves." + } +] diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index 7b697555e..774036c20 100644 --- a/lib/tasks/seeds_helper.rb +++ b/lib/tasks/seeds_helper.rb @@ -17,9 +17,12 @@ module SeedsHelper # Public Blocks template for Experience CS project preview (unowned, loadable without auth). PROJECT_PREVIEW_IDENTIFIER = 'excs-preview-starter' PROJECT_PREVIEW_LOCALE = 'en' + PROJECT_PREVIEW_LOCALE_FR = 'fr-FR' PROJECT_PREVIEW_NAME = 'Experience CS Preview Starter' + PROJECT_PREVIEW_NAME_FR = 'Démo Aperçu Experience CS' PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter.json') PROJECT_PREVIEW_INSTRUCTIONS_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions.json') + PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions_fr.json') def create_school(creator_id, school_id = nil) School.find_or_create_by!(creator_id:, id: school_id) do |school| @@ -124,16 +127,31 @@ def create_project(user_id, school, lesson, code = '') end def create_public_scratch_preview_project + [ + { + locale: PROJECT_PREVIEW_LOCALE, + name: PROJECT_PREVIEW_NAME, + instructions: public_scratch_preview_instructions + }, + { + locale: PROJECT_PREVIEW_LOCALE_FR, + name: PROJECT_PREVIEW_NAME_FR, + instructions: public_scratch_preview_instructions_fr + } + ].map { |attrs| upsert_public_scratch_preview_project(**attrs) } + end + + def upsert_public_scratch_preview_project(locale:, name:, instructions:) project = Project.find_or_initialize_by( identifier: PROJECT_PREVIEW_IDENTIFIER, - locale: PROJECT_PREVIEW_LOCALE + locale: ) - Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}'..." - project.name = PROJECT_PREVIEW_NAME + Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})..." + project.name = name project.user_id = nil project.school = nil project.project_type = Project::Types::CODE_EDITOR_SCRATCH - project.instructions = public_scratch_preview_instructions + project.instructions = instructions if project.scratch_component project.scratch_component.content = public_scratch_preview_content else @@ -144,7 +162,7 @@ def create_public_scratch_preview_project end def destroy_public_scratch_preview_project - Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER, locale: PROJECT_PREVIEW_LOCALE).destroy_all + Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER).destroy_all end def public_scratch_preview_content @@ -154,4 +172,8 @@ def public_scratch_preview_content def public_scratch_preview_instructions JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_PATH)) end + + def public_scratch_preview_instructions_fr + JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH)) + end end diff --git a/spec/lib/test_seeds_spec.rb b/spec/lib/test_seeds_spec.rb index fc6f9ac1f..7efa87204 100644 --- a/spec/lib/test_seeds_spec.rb +++ b/spec/lib/test_seeds_spec.rb @@ -47,14 +47,18 @@ user_id: nil, name: SeedsHelper::PROJECT_PREVIEW_NAME ) + create( + :scratch_project, + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE_FR, + user_id: nil, + name: SeedsHelper::PROJECT_PREVIEW_NAME_FR + ) task.invoke expect( - Project.where( - identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, - locale: SeedsHelper::PROJECT_PREVIEW_LOCALE - ) + Project.where(identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER) ).not_to exist end @@ -98,6 +102,24 @@ ) end + it 'creates a French locale public Scratch preview project' do + project = Project.find_by!( + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE_FR + ) + + expect(project).to have_attributes( + name: SeedsHelper::PROJECT_PREVIEW_NAME_FR, + user_id: nil, + school_id: nil, + project_type: Project::Types::CODE_EDITOR_SCRATCH + ) + expect(project.scratch_component).to be_present + expect(project.instructions).to eq( + JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH)) + ) + end + it 'updates instructions on an existing public Scratch preview project' do project = Project.find_by!( identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, From 48878bbfd697bada7629fe0fa45d7ebe7ff83a7e Mon Sep 17 00:00:00 2001 From: Nathan Richards Date: Thu, 13 Aug 2026 20:05:59 +0200 Subject: [PATCH 3/4] refactor: extract ExCS preview seeds to fix module length Move the public Scratch preview seed helpers out of SeedsHelper so RuboCop Metrics/ModuleLength stays under the limit. --- lib/tasks/project_preview_seeds_helper.rb | 64 ++++++++++++++++++++++ lib/tasks/seeds_helper.rb | 65 ++--------------------- 2 files changed, 68 insertions(+), 61 deletions(-) create mode 100644 lib/tasks/project_preview_seeds_helper.rb diff --git a/lib/tasks/project_preview_seeds_helper.rb b/lib/tasks/project_preview_seeds_helper.rb new file mode 100644 index 000000000..6ef48f566 --- /dev/null +++ b/lib/tasks/project_preview_seeds_helper.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module ProjectPreviewSeedsHelper + # Public Blocks template for Experience CS project preview (unowned, loadable without auth). + PROJECT_PREVIEW_IDENTIFIER = 'excs-preview-starter' + PROJECT_PREVIEW_LOCALE = 'en' + PROJECT_PREVIEW_LOCALE_FR = 'fr-FR' + PROJECT_PREVIEW_NAME = 'Experience CS Preview Starter' + PROJECT_PREVIEW_NAME_FR = 'Démo Aperçu Experience CS' + PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter.json') + PROJECT_PREVIEW_INSTRUCTIONS_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions.json') + PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions_fr.json') + + def create_public_scratch_preview_project + [ + { + locale: PROJECT_PREVIEW_LOCALE, + name: PROJECT_PREVIEW_NAME, + instructions: public_scratch_preview_instructions + }, + { + locale: PROJECT_PREVIEW_LOCALE_FR, + name: PROJECT_PREVIEW_NAME_FR, + instructions: public_scratch_preview_instructions_fr + } + ].map { |attrs| upsert_public_scratch_preview_project(**attrs) } + end + + def upsert_public_scratch_preview_project(locale:, name:, instructions:) + project = Project.find_or_initialize_by( + identifier: PROJECT_PREVIEW_IDENTIFIER, + locale: + ) + Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})..." + project.name = name + project.user_id = nil + project.school = nil + project.project_type = Project::Types::CODE_EDITOR_SCRATCH + project.instructions = instructions + if project.scratch_component + project.scratch_component.content = public_scratch_preview_content + else + project.scratch_component = ScratchComponent.new(content: public_scratch_preview_content) + end + project.save! + project + end + + def destroy_public_scratch_preview_project + Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER).destroy_all + end + + def public_scratch_preview_content + JSON.parse(File.read(PROJECT_PREVIEW_CONTENT_PATH)) + end + + def public_scratch_preview_instructions + JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_PATH)) + end + + def public_scratch_preview_instructions_fr + JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH)) + end +end diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index 774036c20..0bdb27ae1 100644 --- a/lib/tasks/seeds_helper.rb +++ b/lib/tasks/seeds_helper.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true +require_relative 'project_preview_seeds_helper' + module SeedsHelper + include ProjectPreviewSeedsHelper + TEST_USERS = { jane_doe: '583ba872-b16e-46e1-9f7d-df89d267550d', # jane.doe@example.com john_doe: 'bbb9b8fd-f357-4238-983d-6f87b99bdbb2', # john.doe@example.com @@ -14,16 +18,6 @@ module SeedsHelper TEST_SCHOOL = 'e52de409-9210-4e94-b08c-dd11439e07d9' # e52de409-9210-4e94-b08c-dd11439e07d9 SCHOOL_CODE = '12-34-56' - # Public Blocks template for Experience CS project preview (unowned, loadable without auth). - PROJECT_PREVIEW_IDENTIFIER = 'excs-preview-starter' - PROJECT_PREVIEW_LOCALE = 'en' - PROJECT_PREVIEW_LOCALE_FR = 'fr-FR' - PROJECT_PREVIEW_NAME = 'Experience CS Preview Starter' - PROJECT_PREVIEW_NAME_FR = 'Démo Aperçu Experience CS' - PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter.json') - PROJECT_PREVIEW_INSTRUCTIONS_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions.json') - PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions_fr.json') - def create_school(creator_id, school_id = nil) School.find_or_create_by!(creator_id:, id: school_id) do |school| Rails.logger.info 'Seeding a school...' @@ -125,55 +119,4 @@ def create_project(user_id, school, lesson, code = '') content: code }) end end - - def create_public_scratch_preview_project - [ - { - locale: PROJECT_PREVIEW_LOCALE, - name: PROJECT_PREVIEW_NAME, - instructions: public_scratch_preview_instructions - }, - { - locale: PROJECT_PREVIEW_LOCALE_FR, - name: PROJECT_PREVIEW_NAME_FR, - instructions: public_scratch_preview_instructions_fr - } - ].map { |attrs| upsert_public_scratch_preview_project(**attrs) } - end - - def upsert_public_scratch_preview_project(locale:, name:, instructions:) - project = Project.find_or_initialize_by( - identifier: PROJECT_PREVIEW_IDENTIFIER, - locale: - ) - Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})..." - project.name = name - project.user_id = nil - project.school = nil - project.project_type = Project::Types::CODE_EDITOR_SCRATCH - project.instructions = instructions - if project.scratch_component - project.scratch_component.content = public_scratch_preview_content - else - project.scratch_component = ScratchComponent.new(content: public_scratch_preview_content) - end - project.save! - project - end - - def destroy_public_scratch_preview_project - Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER).destroy_all - end - - def public_scratch_preview_content - JSON.parse(File.read(PROJECT_PREVIEW_CONTENT_PATH)) - end - - def public_scratch_preview_instructions - JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_PATH)) - end - - def public_scratch_preview_instructions_fr - JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH)) - end end From 96b1b0defa0cf8fa5227a17b01ba9c5a4360785c Mon Sep 17 00:00:00 2001 From: Nathan Richards Date: Thu, 13 Aug 2026 20:38:57 +0200 Subject: [PATCH 4/4] chore: copilot review changes --- lib/tasks/project_preview_seeds_helper.rb | 18 ++++++++------ lib/tasks/test_seeds.rake | 7 +++--- spec/lib/test_seeds_spec.rb | 29 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/tasks/project_preview_seeds_helper.rb b/lib/tasks/project_preview_seeds_helper.rb index 6ef48f566..e09590017 100644 --- a/lib/tasks/project_preview_seeds_helper.rb +++ b/lib/tasks/project_preview_seeds_helper.rb @@ -31,23 +31,27 @@ def upsert_public_scratch_preview_project(locale:, name:, instructions:) identifier: PROJECT_PREVIEW_IDENTIFIER, locale: ) + raise "Refusing to overwrite non-public project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})" if project.persisted? && (project.user_id.present? || project.school_id.present?) + Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})..." project.name = name project.user_id = nil - project.school = nil + project.school_id = nil project.project_type = Project::Types::CODE_EDITOR_SCRATCH project.instructions = instructions - if project.scratch_component - project.scratch_component.content = public_scratch_preview_content - else - project.scratch_component = ScratchComponent.new(content: public_scratch_preview_content) - end + project.scratch_component ||= ScratchComponent.new + project.scratch_component.content = public_scratch_preview_content project.save! project end def destroy_public_scratch_preview_project - Project.where(identifier: PROJECT_PREVIEW_IDENTIFIER).destroy_all + Project.where( + identifier: PROJECT_PREVIEW_IDENTIFIER, + user_id: nil, + school_id: nil, + project_type: Project::Types::CODE_EDITOR_SCRATCH + ).destroy_all end def public_scratch_preview_content diff --git a/lib/tasks/test_seeds.rake b/lib/tasks/test_seeds.rake index f3183b862..76abef66c 100644 --- a/lib/tasks/test_seeds.rake +++ b/lib/tasks/test_seeds.rake @@ -48,13 +48,14 @@ namespace :test_seeds do desc 'Create a school with lessons and students' task create: :environment do - create_public_scratch_preview_project - if School.exists?(id: TEST_SCHOOL) - puts "Test school (#{TEST_SCHOOL}) already exists, run the destroy_seed_data task to start over)." + create_public_scratch_preview_project + puts "Test school (#{TEST_SCHOOL}) already exists, run the destroy_seed_data task to start over." else ActiveRecord::Base.transaction do Rails.logger.info 'Attempting to seed data...' + create_public_scratch_preview_project + creator_id = ENV.fetch('SEEDING_CREATOR_ID', TEST_USERS[:jane_doe]) teacher_id = ENV.fetch('SEEDING_TEACHER_ID', TEST_USERS[:john_doe]) diff --git a/spec/lib/test_seeds_spec.rb b/spec/lib/test_seeds_spec.rb index 7efa87204..1dd11cb5e 100644 --- a/spec/lib/test_seeds_spec.rb +++ b/spec/lib/test_seeds_spec.rb @@ -62,6 +62,19 @@ ).not_to exist end + it 'does not destroy owned projects that share the preview identifier' do + owned = create( + :scratch_project, + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE, + user_id: SecureRandom.uuid + ) + + task.invoke + + expect(Project.find_by(id: owned.id)).to be_present + end + it 'removes all feature flags' do Flipper.enable(:some_feature) task.invoke @@ -135,6 +148,22 @@ ) end + it 'refuses to overwrite a non-public project with the preview identifier' do + Project.where(identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER).destroy_all + create( + :scratch_project, + identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER, + locale: SeedsHelper::PROJECT_PREVIEW_LOCALE, + user_id: SecureRandom.uuid + ) + + task.reenable + expect { task.invoke }.to raise_error( + RuntimeError, + /Refusing to overwrite non-public project/ + ) + end + it 'creates lessons with projects' do school = School.find_by(creator_id:) expect(SchoolClass.where(school_id: school.id)).to exist