From 0ef89f670ede6ce717c57a10c58decdcd058d568 Mon Sep 17 00:00:00 2001 From: gangelo Date: Thu, 15 Feb 2024 16:43:33 -0500 Subject: [PATCH] Migration and migration specs --- .../migration/{service.rb => base_service.rb} | 30 ++-- lib/dsu/migration/factory.rb | 9 +- lib/dsu/migration/raw_json_file.rb | 15 ++ lib/dsu/migration/raw_json_files.rb | 56 +++++++ lib/dsu/migration/service_20230613121411.rb | 107 ------------- lib/dsu/migration/service_20240210161248.rb | 121 -------------- lib/dsu/migration/v20230613121411/service.rb | 76 +++++++++ .../v20240210161248/color_theme_hash.rb | 13 ++ .../v20240210161248/configuration_hash.rb | 15 ++ .../v20240210161248/entry_group_hash.rb | 13 ++ lib/dsu/migration/v20240210161248/service.rb | 148 ++++++++++++++++++ lib/dsu/models/color_theme.rb | 2 + lib/dsu/support/fileable.rb | 4 +- lib/locales/en/miscellaneous.yml | 2 - spec/dsu/migration/factory_spec.rb | 8 +- .../migration/service20230613121411_spec.rb | 129 --------------- .../migration/v20230613121411/service_spec.rb | 92 +++++++++++ .../service_spec.rb} | 38 +---- .../support/mock_migration_version_helpers.rb | 37 +++-- 19 files changed, 489 insertions(+), 426 deletions(-) rename lib/dsu/migration/{service.rb => base_service.rb} (63%) create mode 100644 lib/dsu/migration/raw_json_file.rb create mode 100644 lib/dsu/migration/raw_json_files.rb delete mode 100644 lib/dsu/migration/service_20230613121411.rb delete mode 100644 lib/dsu/migration/service_20240210161248.rb create mode 100644 lib/dsu/migration/v20230613121411/service.rb create mode 100644 lib/dsu/migration/v20240210161248/color_theme_hash.rb create mode 100644 lib/dsu/migration/v20240210161248/configuration_hash.rb create mode 100644 lib/dsu/migration/v20240210161248/entry_group_hash.rb create mode 100644 lib/dsu/migration/v20240210161248/service.rb delete mode 100644 spec/dsu/migration/service20230613121411_spec.rb create mode 100644 spec/dsu/migration/v20230613121411/service_spec.rb rename spec/dsu/migration/{service20240210161248_spec.rb => v20240210161248/service_spec.rb} (56%) diff --git a/lib/dsu/migration/service.rb b/lib/dsu/migration/base_service.rb similarity index 63% rename from lib/dsu/migration/service.rb rename to lib/dsu/migration/base_service.rb index d466ea8e..df784715 100644 --- a/lib/dsu/migration/service.rb +++ b/lib/dsu/migration/base_service.rb @@ -6,7 +6,7 @@ module Dsu module Migration - class Service + class BaseService include Support::Fileable def initialize(options: {}) @@ -17,12 +17,12 @@ def migrate_if! return unless run_migration? puts "Running migrations #{from_migration_version} -> #{to_migration_version}..." - puts "pretend?: #{pretend?}" if pretend? + puts "\tpretend?: #{pretend?}" if pretend? run_migration! update_migration_version! - puts "Migration #{from_migration_version} -> #{to_migration_version} complete." + puts "\tMigration #{from_migration_version} -> #{to_migration_version} complete." end private @@ -54,16 +54,19 @@ def to_migration_version # The migration version before running this migration. def migration_version + # Typically we should not be using models in any of the migration services + # because if these change, the migrations will break. However, using + # the MigrationVersion model is an exception because it is a very simple + # model and is unlikely to change. @migration_version ||= Models::MigrationVersion.new.version end def create_backup puts "Creating backup #{backup_folder}..." - return puts 'Skipping: backup already exists.' if backup_exist? + return puts "\tSkipping: backup already exists." if backup_exist? - FileUtils.cp_r(dsu_folder, backup_folder) unless pretend? - puts 'Done.' + FileUtils.cp_r(dsu_folder, backup_folder) end def backup_exist? @@ -74,13 +77,20 @@ def backup_folder @backup_folder ||= File.join(root_folder, "dsu-#{from_migration_version}-backup") end - def update_migration_version + def update_migration_version! puts 'Updating migration version...' - puts - return if pretend? || migration_version == Migration::VERSION + return if pretend? || migration_version == to_migration_version - Models::MigrationVersion.new(version: Migration::VERSION).save! + Models::MigrationVersion.new(version: to_migration_version).save! + end + + def seed_data_folder + seed_data_dsu_folder_for(migration_version: to_migration_version) + end + + def raise_backup_folder_does_not_exist_error_if! + raise "Backup folder #{backup_folder} does not exist, cannot continue" unless backup_exist? end end end diff --git a/lib/dsu/migration/factory.rb b/lib/dsu/migration/factory.rb index 2a14187b..57fb378a 100644 --- a/lib/dsu/migration/factory.rb +++ b/lib/dsu/migration/factory.rb @@ -1,15 +1,16 @@ # frozen_string_literal: true -require_relative 'service_20230613121411' -require_relative 'service_20240210161248' +require_relative 'v20230613121411/service' +require_relative 'v20240210161248/service' module Dsu module Migration class Factory class << self def migrate_if!(options: {}) - Service20230613121411.new(options: options).migrate_if! - Service20240210161248.new(options: options).migrate_if! + V20230613121411::Service.new(options: options).migrate_if! + binding.pry + V20240210161248::Service.new(options: options).migrate_if! rescue StandardError => e puts I18n.t('migrations.error.failed', message: e.message) exit 1 diff --git a/lib/dsu/migration/raw_json_file.rb b/lib/dsu/migration/raw_json_file.rb new file mode 100644 index 00000000..5eb69673 --- /dev/null +++ b/lib/dsu/migration/raw_json_file.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative '../crud/json_file' + +module Dsu + module Migration + class RawJsonFile < Crud::JsonFile + public :read, :read!, :version= + + def to_h + read.merge(version: version) + end + end + end +end diff --git a/lib/dsu/migration/raw_json_files.rb b/lib/dsu/migration/raw_json_files.rb new file mode 100644 index 00000000..568b83e0 --- /dev/null +++ b/lib/dsu/migration/raw_json_files.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'pathname' + +require_relative 'raw_json_file' + +module Dsu + module Migration + class RawJsonFiles + attr_reader :folder + + def initialize(folder) + @folder = folder + end + + def each_file(regex: //) + return unless folder_exist? + + Pathname.new(folder).children.each do |child| + next unless child.file? && child.to_s.match?(regex) + + yield RawJsonFile.new(child) + end + end + + def folder_exist? + self.class.folder_exist?(folder: folder) + end + + class << self + def folder_exist?(folder:) + Dir.exist?(folder) + end + end + + private + + attr_writer :folder + + def safe_cp_r(source, destination) + Pathname.new(source).find do |source_path| + next if source_path.directory? + + relative_path = source_path.relative_path_from(Pathname.new(source)) + target_path = Pathname.new(destination).join(relative_path) + + next if target_path.exist? + + FileUtils.mkdir_p(target_path.dirname) + FileUtils.cp(source_path, target_path) + end + end + end + end +end diff --git a/lib/dsu/migration/service_20230613121411.rb b/lib/dsu/migration/service_20230613121411.rb deleted file mode 100644 index e3799a1a..00000000 --- a/lib/dsu/migration/service_20230613121411.rb +++ /dev/null @@ -1,107 +0,0 @@ -# frozen_string_literal: true - -require_relative 'service' -require_relative 'version' - -module Dsu - module Migration - class Service20230613121411 < Service - private - - def run_migration! - super - - raise "Backup folder #{backup_folder} does not exist, cannot continue" unless backup_exist? - - # TODO: save old dsu entries to migrate them to the new dsu entries - delete_old_config_file - delete_old_entries_folder - delete_old_themes_folder - - create_new_dsu_folders - end - - def from_migration_version - 0 - end - - def to_migration_version - 20230613121411 # rubocop:disable Style/NumericLiterals - end - - # From files/folders - - def config_file_from - File.join(root_folder, '.dsu') - end - - def dsu_folder_from - File.join(root_folder, 'dsu') - end - - def entries_folder_from - File.join(dsu_folder_from, 'entries') - end - - def themes_folder_from - File.join(dsu_folder_from, 'themes') - end - - # To folders - - def dsu_folder_to - File.join(root_folder, 'dsu') - end - - def entries_folder_to - File.join(dsu_folder_to, 'entries') - end - - def themes_folder_to - File.join(dsu_folder_to, 'entries') - end - - def seed_data_folder20230613121411 - File.join(seed_data_folder, to_migration_version) - end - - def create_new_dsu_folders - puts 'Creating new dsu folders...' - puts - - if pretend? - FileUtils.cp_r(File.join(seed_data_folder20230613121411, '.'), - root_folder, noop: true, verbose: true) - else - FileUtils.cp_r(File.join(seed_data_folder20230613121411, '.'), - root_folder) - end - end - - # Deletes - - def delete_old_config_file - puts 'Deleting old configuration file...' - puts - - return if pretend? - - File.delete(config_file_from) if File.file?(config_file_from) - end - - def delete_old_entries_folder - puts 'Deleting old entry folder...' - puts - - FileUtils.rm_rf(entries_folder_from) unless pretend? - end - - def delete_old_themes_folder - puts 'Deleting old themes folder...' - puts - - FileUtils.rm_rf(themes_folder_from) unless pretend? - end - end - end -end diff --git a/lib/dsu/migration/service_20240210161248.rb b/lib/dsu/migration/service_20240210161248.rb deleted file mode 100644 index 92d5fe15..00000000 --- a/lib/dsu/migration/service_20240210161248.rb +++ /dev/null @@ -1,121 +0,0 @@ -# frozen_string_literal: true - -require_relative 'service' -require_relative 'version' - -module Dsu - module Migration - class Service20240210161248 < Service - private - - def run_migration! - super - - add_new_color_themes - create_default_project - update_configuration - update_entry_groups - update_color_themes - delete_old_entry_folder - - puts 'Migration completed successfully.' - end - - def from_migration_version - 20230613121411 # rubocop:disable Style/NumericLiterals - end - - def to_migration_version - 20240210161248 # rubocop:disable Style/NumericLiterals - end - - def add_new_color_themes - puts 'Copying new color themes...' - puts - - %w[light.json christmas.json].each do |theme_file| - destination_theme_file_path = File.join(Dsu::Support::Fileable.themes_folder, theme_file) - # next if File.exist?(destination_theme_file_path) - - source_theme_file_path = File.join(Dsu::Support::Fileable.seed_data_folder, 'themes', theme_file) - FileUtils.cp(source_theme_file_path, destination_theme_file_path) unless pretend? - puts I18n.t('migrations.information.theme_copied', - from: source_theme_file_path, to: destination_theme_file_path) - end - end - - def create_default_project - default_project = Models::Configuration::DEFAULT_CONFIGURATION[:default_project] - return if Models::Project.project_initialized?(project_name: default_project) - - puts "Creating default project \"#{default_project}\"..." - puts - - Models::Project.create(project_name: default_project, options: options) unless pretend? - end - - def update_configuration - puts 'Updating configuration...' - puts - - return if pretend? - - Models::Configuration.new.tap do |configuration| - configuration.version = Dsu::Migration::VERSION - configuration.write! - end - end - - def update_entry_groups - puts 'Updating entry groups...' - puts - - return if pretend? || Dir.exist?(entries_folder) - - puts 'Copying entries to default project...' - puts - - FileUtils.mkdir_p(entries_folder) - FileUtils.cp_r(File.join(backup_folder, 'entries', '.'), entries_folder) - - puts 'Updating entry group version...' - puts - - Models::EntryGroup.all.each do |entry_group| - puts "Updating entry group version: #{entry_group.time_yyyy_mm_dd}..." - entry_group.version = Dsu::Migration::VERSION - entry_group.save! unless pretend? - end - end - - def update_color_themes - puts 'Updating color themes...' - puts - - puts 'Copying color themes...' - puts - - unless pretend? - FileUtils.mkdir_p(themes_folder) - FileUtils.cp_r(File.join(backup_folder, 'themes', '.'), themes_folder) - end - - puts 'Updating color theme version...' - puts - - Models::ColorTheme.all.each do |color_theme| - puts "Updating color theme version: #{color_theme.theme_name}..." - color_theme.update_version! - color_theme.save! unless pretend? - end - end - - def delete_old_entry_folder - puts 'Cleaning up old entries...' - puts - - FileUtils.rm_rf(File.join(dsu_folder, 'entries')) unless pretend? - end - end - end -end diff --git a/lib/dsu/migration/v20230613121411/service.rb b/lib/dsu/migration/v20230613121411/service.rb new file mode 100644 index 00000000..b86cc751 --- /dev/null +++ b/lib/dsu/migration/v20230613121411/service.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require_relative '../base_service' +require_relative '../version' + +module Dsu + module Migration + module V20230613121411 + class Service < BaseService + private + + def run_migration! + super + + raise_backup_folder_does_not_exist_error_if! + + delete_old_config_file + delete_old_entries_folder + delete_old_themes_folder + + create_new_dsu_folders + end + + def from_migration_version + 0 + end + + def to_migration_version + 20230613121411 # rubocop:disable Style/NumericLiterals + end + + def config_file_from + File.join(root_folder, '.dsu') + end + + def dsu_folder_from + File.join(root_folder, 'dsu') + end + + def entries_folder_from + File.join(dsu_folder_from, 'entries') + end + + def themes_folder_from + File.join(dsu_folder_from, 'themes') + end + + def create_new_dsu_folders + puts 'Creating new dsu folders...' + + FileUtils.cp_r(File.join(seed_data_folder, '.'), File.join(root_folder, 'dsu')) unless pretend? + end + + def delete_old_config_file + puts 'Deleting old configuration file...' + + return if pretend? + + File.delete(config_file_from) if File.file?(config_file_from) + end + + def delete_old_entries_folder + puts 'Deleting old entry folder...' + + FileUtils.rm_rf(entries_folder_from) unless pretend? + end + + def delete_old_themes_folder + puts 'Deleting old themes folder...' + + FileUtils.rm_rf(themes_folder_from) unless pretend? + end + end + end + end +end diff --git a/lib/dsu/migration/v20240210161248/color_theme_hash.rb b/lib/dsu/migration/v20240210161248/color_theme_hash.rb new file mode 100644 index 00000000..3e997c68 --- /dev/null +++ b/lib/dsu/migration/v20240210161248/color_theme_hash.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Dsu + module Migration + module V20240210161248 + module ColorThemeHash + def to_h + read.merge(version: version) + end + end + end + end +end diff --git a/lib/dsu/migration/v20240210161248/configuration_hash.rb b/lib/dsu/migration/v20240210161248/configuration_hash.rb new file mode 100644 index 00000000..f23b2ef2 --- /dev/null +++ b/lib/dsu/migration/v20240210161248/configuration_hash.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Dsu + module Migration + module V20240210161248 + module ConfigurationHash + attr_accessor :default_project + + def to_h + read.merge(version: version, default_project: default_project) + end + end + end + end +end diff --git a/lib/dsu/migration/v20240210161248/entry_group_hash.rb b/lib/dsu/migration/v20240210161248/entry_group_hash.rb new file mode 100644 index 00000000..224fda6a --- /dev/null +++ b/lib/dsu/migration/v20240210161248/entry_group_hash.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Dsu + module Migration + module V20240210161248 + module EntryGroupHash + def to_h + read.merge(version: version) + end + end + end + end +end diff --git a/lib/dsu/migration/v20240210161248/service.rb b/lib/dsu/migration/v20240210161248/service.rb new file mode 100644 index 00000000..2c617973 --- /dev/null +++ b/lib/dsu/migration/v20240210161248/service.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +require_relative '../base_service' +require_relative '../raw_json_file' +require_relative '../version' +require_relative 'color_theme_hash' +require_relative 'configuration_hash' +require_relative 'entry_group_hash' + +module Dsu + module Migration + module V20240210161248 + class Service < BaseService + private + + def run_migration! + super + + raise_backup_folder_does_not_exist_error_if! + + add_new_color_themes + create_default_project + create_current_project_file + update_configuration + update_entry_groups + update_color_themes + delete_old_entry_folder + + puts 'Migration completed successfully.' + end + + def from_migration_version + 20230613121411 # rubocop:disable Style/NumericLiterals + end + + def to_migration_version + 20240210161248 # rubocop:disable Style/NumericLiterals + end + + def config_file_from + File.join(root_folder, '.dsu') + end + + def dsu_folder_from + File.join(root_folder, 'dsu') + end + + def entries_folder_from + File.join(dsu_folder_from, 'entries') + end + + def themes_folder_from + File.join(dsu_folder_from, 'themes') + end + + def add_new_color_themes + puts 'Copying new color themes...' + + %w[light.json christmas.json].each do |theme_file| + destination_theme_file_path = File.join(themes_folder_from, theme_file) + # Don't skip these theme files because they were deployed in the previous + # dsu version with bugs. We need to overwrite them with this new version. + # next if File.exist?(destination_theme_file_path) + + source_theme_file_path = File.join(seed_data_folder, 'themes', theme_file) + FileUtils.cp(source_theme_file_path, destination_theme_file_path) unless pretend? + end + end + + def create_default_project + puts "Creating default project \"#{default_project_name}\"..." + + return if pretend? + + FileUtils.cp_r(File.join(seed_data_folder, 'projects', '.'), + File.join(dsu_folder, 'projects')) + end + + def create_current_project_file + puts 'Creating current project file...' + + return if pretend? + + # NOTE: dsu_folder won't change and is safe to use here. + FileUtils.cp(File.join(seed_data_folder, 'current_project.json'), dsu_folder) + end + + def update_configuration + puts 'Updating configuration...' + + return if pretend? + + # NOTE: config_path won't change and is safe to use here. + RawJsonFile.new(config_path).tap do |configuration_file| + configuration_file.extend(ConfigurationHash) + configuration_file.version = to_migration_version + configuration_file.default_project = default_project_name + end.save! + end + + def update_entry_groups + puts 'Updating entry groups...' + + return if pretend? + + puts "\tCopying entries to default project \"#{default_project_name}\"..." + + entries_folder_to = File.join(dsu_folder, 'projects', default_project_name, 'entries') + FileUtils.cp_r(File.join(entries_folder_from, '.'), entries_folder_to) + + puts "\tUpdating entry group version..." + + RawJsonFiles.new(entries_folder_to).each_file(regex: /\d{4}-\d{2}-\d{2}.json/) do |raw_entry_group| + raw_entry_group.extend(EntryGroupHash) + raw_entry_group.version = to_migration_version + raw_entry_group.save! + end + end + + def update_color_themes + puts 'Updating color themes...' + + FileUtils.cp_r(File.join(backup_folder, 'themes', '.'), themes_folder) unless pretend? + + puts "\tUpdating color theme version..." + + themes_folder_to = File.join(dsu_folder, 'themes') + + RawJsonFiles.new(themes_folder_to).each_file(regex: /.+.json/) do |raw_entry_group| + raw_entry_group.extend(ColorThemeHash) + raw_entry_group.version = to_migration_version + raw_entry_group.save! unless pretend? + end + end + + def delete_old_entry_folder + puts 'Cleaning up old entries...' + + FileUtils.rm_rf(File.join(entries_folder_from)) unless pretend? + end + + def default_project_name + 'default' + end + end + end + end +end diff --git a/lib/dsu/models/color_theme.rb b/lib/dsu/models/color_theme.rb index a8fa1433..1306a19a 100644 --- a/lib/dsu/models/color_theme.rb +++ b/lib/dsu/models/color_theme.rb @@ -20,6 +20,8 @@ class ColorTheme < Crud::JsonFile include Support::Fileable include Support::Presentable + THEME_FILE_NAME_REGEX = /.+.json/ + VERSION = Migration::VERSION DEFAULT_THEME_NAME = 'default' diff --git a/lib/dsu/support/fileable.rb b/lib/dsu/support/fileable.rb index 823e5c4b..3b8b69a1 100644 --- a/lib/dsu/support/fileable.rb +++ b/lib/dsu/support/fileable.rb @@ -85,8 +85,8 @@ def backup_folder(version:) # Seed data folders - def seed_data_folder - File.join(gem_dir, 'lib/seed_data') + def seed_data_dsu_folder_for(migration_version:) + File.join(gem_dir, 'lib/seed_data', migration_version.to_s, 'dsu') end # Projects diff --git a/lib/locales/en/miscellaneous.yml b/lib/locales/en/miscellaneous.yml index 36c2aee1..d8299a1f 100644 --- a/lib/locales/en/miscellaneous.yml +++ b/lib/locales/en/miscellaneous.yml @@ -26,5 +26,3 @@ en: migrations: error: failed: "Error running migrations: %{message}" - information: - theme_copied: Theme copied from %{from} to %{to}. diff --git a/spec/dsu/migration/factory_spec.rb b/spec/dsu/migration/factory_spec.rb index 5429dbd1..ce48c27b 100644 --- a/spec/dsu/migration/factory_spec.rb +++ b/spec/dsu/migration/factory_spec.rb @@ -10,28 +10,28 @@ context 'when the migration version is not 20230613121411' do before do - allow(Dsu::Migration::Service20240210161248).to receive(:new) # Stub the :new method + allow(Dsu::Migration::V20240210161248::Service).to receive(:new) # Stub the :new method described_class.migrate_if!(options: options) end let(:version) { 0 } it 'does not call the migration service' do - expect(Dsu::Migration::Service20240210161248).to_not have_received(:new) + expect(Dsu::Migration::V20240210161248::Service).to_not have_received(:new) end end context 'when the migration version is 20230613121411' do before do create(:migration_version, version: 20230613121411) # rubocop:disable Style/NumericLiterals - allow(Dsu::Migration::Service20240210161248).to receive(:new).and_call_original # Stub :new but allow it to call the original method + allow(Dsu::Migration::V20240210161248::Service).to receive(:new).and_call_original # Stub :new but allow it to call the original method described_class.migrate_if!(options: options) end let(:version) { 20230613121411 } # rubocop:disable Style/NumericLiterals it 'calls the migration service' do - expect(Dsu::Migration::Service20240210161248).to have_received(:new) + expect(Dsu::Migration::V20240210161248::Service).to have_received(:new) end end end diff --git a/spec/dsu/migration/service20230613121411_spec.rb b/spec/dsu/migration/service20230613121411_spec.rb deleted file mode 100644 index 629b98c0..00000000 --- a/spec/dsu/migration/service20230613121411_spec.rb +++ /dev/null @@ -1,129 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Dsu::Migration::Service20230613121411 do - subject(:service) { described_class.new(options: options) } - - shared_examples 'the migration is successful' do - it 'backs up the old config file' do - expect(File.exist?(File.join(backup_folder, Dsu::Support::Fileable.config_file_name))).to be true - end - - it 'backs up the old entry files' do - entries_folder = File.basename(Dsu::Support::Fileable.entries_folder) - expect(File.exist?(File.join(backup_folder, entries_folder, Dsu::Support::Fileable.entries_file_name(time: time)))).to be true - end - - it 'backs up the old theme files' do - themes_folder = File.basename(Dsu::Support::Fileable.themes_folder) - expect(File.exist?(File.join(backup_folder, themes_folder, Dsu::Support::Fileable.theme_file_name(theme_name: theme_name)))).to be true - end - - it 'creates the new config file' do - expect(File.exist?(Dsu::Support::Fileable.config_path)).to be true - end - - it 'copies the new theme files' do - theme_names = %w[cherry default lemon matrix whiteout] - themes_exist = theme_names.all? { |theme_name| File.exist?(Dsu::Support::Fileable.themes_path(theme_name: theme_name)) } - expect(themes_exist).to be true - end - - it 'creates an initial entry group' do - expect(File.exist?(Dsu::Support::Fileable.entries_path(time: time))).to be true - end - end - - let(:options) { {} } - - describe 'class methods' do - describe '.run_migrations?' do - context 'when the migration version is current' do - before do - create(:migration_version, :with_current_version) - end - - it 'returns false' do - expect(described_class.run_migrations?).to be(false) - end - end - - context 'when the migration version less than 20230613121411' do - before do - create(:migration_version, version: 20230613121411 - 1) # rubocop:disable Style/NumericLiterals - end - - it 'returns true' do - expect(described_class.run_migrations?).to be(true) - end - end - - context 'when the migration version greater than the current version' do - before do - create(:migration_version, version: Dsu::Migration::VERSION + 1) - end - - it 'returns false' do - expect(described_class.run_migrations?).to be(false) - end - end - end - end - - context 'when migrations should not be run' do - subject(:service) { build(:migration_service) } - - before do - create(:migration_version, :with_current_version) - end - - specify 'the migration version file exists' do - migration_version = create(:migration_version, :with_current_version) - expect(migration_version).to exist - end - - it 'does not run migrations' do - expect { service.call }.to output(/Nothing to do/).to_stdout - end - end - - context 'when migrations should be run' do - subject(:service) { build(:migration_service) } - - before do - FileUtils.touch(Dsu::Support::Fileable.config_path) - FileUtils.touch(Dsu::Support::Fileable.entries_path(time: time)) - FileUtils.touch(Dsu::Support::Fileable.themes_path(theme_name: theme_name)) - end - - let(:time) { Time.now.in_time_zone } - let(:theme_name) { 'old_theme' } - let(:backup_folder) { Dsu::Support::Fileable.backup_folder(version: 0) } - - context 'when the migration file exists' do - before do - service.call - end - - specify 'the migration version file exists' do - migration_version = build(:migration_version, version: 0) - expect(migration_version).to exist - end - - it_behaves_like 'the migration is successful' - end - - context 'when the migration file does not exist' do - before do - service.call - end - - specify 'the migration version file does not exist' do - migration_version = build(:migration_version) - migration_version.delete - expect(migration_version).to_not exist - end - - it_behaves_like 'the migration is successful' - end - end -end diff --git a/spec/dsu/migration/v20230613121411/service_spec.rb b/spec/dsu/migration/v20230613121411/service_spec.rb new file mode 100644 index 00000000..c1d08a6c --- /dev/null +++ b/spec/dsu/migration/v20230613121411/service_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +RSpec.describe Dsu::Migration::V20230613121411::Service, type: :migration do + subject(:service) { described_class.new(options: options) } + + before do + create(:migration_version, version: migration_version, options: options) + end + + let(:dsu_folder) { File.join(temp_folder, 'dsu') } + + let(:options) { {} } + let(:migration_version) { 0 } + + describe '#initialize' do + let(:migration_version) { 20240210161248 } # rubocop:disable Style/NumericLiterals + + it_behaves_like 'no error is raised' + end + + describe '#migrate_if!' do + subject(:service_migrate_if) { service.migrate_if! } + + context 'when the migration version is not 0' do + before do + mock_migration_version_for(version: migration_version) + service_migrate_if + end + + let(:migration_version) { 20240210161248 } # rubocop:disable Style/NumericLiterals + let(:expected) { File.join('spec', 'fixtures', 'folders', migration_version.to_s) } + let(:actual) { dsu_folder } + + it 'does not make any changes to the dsu folder structure or configuration file' do + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual)).to be(true) + end + end + + context 'when the migration version is 0' do + before do + mock_migration_version_for(version: migration_version) + service_migrate_if + end + + shared_examples 'the migration was run in pretend mode' do + let(:expected) { File.join('spec', 'fixtures', 'folders', migration_version.to_s) } + let(:actual) { dsu_folder } + + it 'does not make any changes to the dsu folder structure or configuration file' do + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual)).to be(true) + end + end + + context 'when the pretend option is implicitly set to true (not provided)' do + it_behaves_like 'the migration was run in pretend mode' + end + + context 'when the pretend option is explicitly set to true' do + let(:options) { { pretend: true } } + + it_behaves_like 'the migration was run in pretend mode' + end + + context 'when the pretend option is set to false' do + let(:options) { { pretend: false } } + let(:expected) { File.join('spec', 'fixtures', 'folders', 20230613121411.to_s) } # rubocop:disable Style/NumericLiterals + let(:actual) { dsu_folder } + + it 'migrates the dsu folder structure and files' do + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual, + known_deleted_files: known_deleted_files, known_added_files: known_added_files)).to be(true) + end + end + end + end + + def known_deleted_files + %w[ + entries/2023-12-28.json + entries/2023-12-29.json + entries/2024-01-01.json + entries/2024-01-02.json + ] + end + + def known_added_files + %w[ + themes/christmas.json + themes/light.json + ] + end +end diff --git a/spec/dsu/migration/service20240210161248_spec.rb b/spec/dsu/migration/v20240210161248/service_spec.rb similarity index 56% rename from spec/dsu/migration/service20240210161248_spec.rb rename to spec/dsu/migration/v20240210161248/service_spec.rb index c1a8eee2..6f7776db 100644 --- a/spec/dsu/migration/service20240210161248_spec.rb +++ b/spec/dsu/migration/v20240210161248/service_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe Dsu::Migration::Service20240210161248, type: :migration do +RSpec.describe Dsu::Migration::V20240210161248::Service, type: :migration do subject(:service) { described_class.new(options: options) } before do @@ -30,7 +30,7 @@ let(:actual) { Dsu::Support::Fileable.dsu_folder } it 'does not make any changes to the dsu folder structure or configuration file' do - expect(dsu_folders_match?(expected: expected, actual: actual)).to be(true) + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual)).to be(true) end end @@ -45,7 +45,7 @@ let(:actual) { Dsu::Support::Fileable.dsu_folder } it 'does not make any changes to the dsu folder structure or configuration file' do - expect(dsu_folders_match?(expected: expected, actual: actual)).to be(true) + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual)).to be(true) end end @@ -65,37 +65,7 @@ let(:actual) { Dsu::Support::Fileable.dsu_folder } it 'migrates the dsu folder structure and configuration file' do - expect(dsu_folders_match?(expected: expected, actual: actual)).to be(true) - end - - it 'sets all the entry group versions to the correct migration version' do - all_entry_groups = Dsu::Models::EntryGroup.all - expect(all_entry_groups.all? do |entry_group| - entry_group.version == 20240210161248 # rubocop:disable Style/NumericLiterals - end).to be(true) - end - - it 'sets all the color theme versions to the correct migration version' do - all_color_themes = Dsu::Models::ColorTheme.all - expect(all_color_themes.all? do |color_theme| - color_theme.version == 20240210161248 # rubocop:disable Style/NumericLiterals - end).to be(true) - end - - it 'sets the configuration file version to the correct migration version' do - expect(Dsu::Models::Configuration.new.version).to eq(20240210161248) # rubocop:disable Style/NumericLiterals - end - - it 'sets the migration version file version to the correct migration version' do - expect(Dsu::Models::MigrationVersion.new.version).to eq(20240210161248) # rubocop:disable Style/NumericLiterals - end - - it 'copies the christmas color theme' do - expect(Dsu::Models::ColorTheme.new(theme_name: 'christmas').exist?).to be(true) - end - - it 'copies the light color theme' do - expect(Dsu::Models::ColorTheme.new(theme_name: 'light').exist?).to be(true) + expect(dsu_folders_and_file_contents_match?(expected: expected, actual: actual)).to be(true) end end end diff --git a/spec/support/mock_migration_version_helpers.rb b/spec/support/mock_migration_version_helpers.rb index 14debff7..5185cce7 100644 --- a/spec/support/mock_migration_version_helpers.rb +++ b/spec/support/mock_migration_version_helpers.rb @@ -12,23 +12,27 @@ def mock_migration_version_for(version:) create_migration_version_for_if(version) end - def dsu_folders_match?(expected:, actual:) - #dsu_folder_contents(expected) == dsu_folder_contents(actual) - dsu_folder_contents_match?(expected: expected, actual: actual) - end - - def dsu_folder_contents_match?(expected:, actual:) - return false unless dsu_folder_contents(expected) == dsu_folder_contents(actual) - + def dsu_folders_and_file_contents_match?(expected:, actual:, known_deleted_files: [], known_added_files: []) expected_files = dsu_folder_contents(expected) actual_files = dsu_folder_contents(actual) + puts "\ndsu_folder_contents(expected)->\n#{expected_files.join("\n")}" + puts "\n\ndsu_folder_contents(actual)->\n#{actual_files.join("\n")}" + puts "\n\nKnown deleted actual files->\n#{known_deleted_files.join("\n")}" + puts "\n\nKnown added expeccted files->\n#{known_added_files.join("\n")}" + + expected_files -= known_deleted_files + actual_files -= known_added_files + + return false unless expected_files == actual_files + expected_files.each_with_index do |expected_file, index| next if File.directory?(File.join(expected, expected_file)) - match = files_match?(expected_file: File.join(expected, expected_file), - actual_file: File.join(actual, actual_files[index])) - return false unless match + return false unless display_no_match_if( + expected_file: File.join(expected, expected_file), + actual_file: File.join(actual, actual_files[index]) + ) end true @@ -36,11 +40,18 @@ def dsu_folder_contents_match?(expected:, actual:) private + def display_no_match_if(expected_file:, actual_file:) + files_match?(expected_file: expected_file, actual_file: actual_file).tap do |match| + puts "\nExpected file -> #{expected_file}\nActual file -> #{actual_file}\nMatch? -> #{match}" + end + end + def files_match?(expected_file:, actual_file:) File.read(expected_file).chomp == File.read(actual_file).chomp end - def dsu_folder_contents(folder, exclude_files = ['.DS_Store']) + def dsu_folder_contents(folder, exclude_files = []) + exclude_files << '.DS_Store' root_path = Pathname.new(folder) contents = [] @@ -69,7 +80,7 @@ def create_mock_dsu_folder_for(migration_version) def create_mock_dsu_configuration_for(migration_version) ext = migration_version.zero? ? 'yaml' : 'json' source_file = File.join('spec', 'fixtures', 'files', 'configurations', "#{migration_version}.#{ext}") - FileUtils.cp(source_file, temp_folder) + FileUtils.cp(source_file, File.join(temp_folder, '.dsu')) end def create_migration_version_for_if(version)