diff --git a/app/views/repositories/index.html.erb b/app/views/repositories/index.html.erb
deleted file mode 100644
index 2aa829f..0000000
--- a/app/views/repositories/index.html.erb
+++ /dev/null
@@ -1,67 +0,0 @@
-
diff --git a/app/views/repositories/new.html.erb b/app/views/repositories/new.html.erb
deleted file mode 100644
index 8a7d372..0000000
--- a/app/views/repositories/new.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 59def3e..f6b07e1 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -1,10 +1,10 @@
- <%= link_to new_user_repository_path(@user),
+ <%= link_to page_path("basic-setup"),
class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" do %>
- Create new repository
+ Create new app
<% end %>
<%= link_to dashboard_path,
class: "inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" do %>
diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc
index feb2227..57a5559 100644
--- a/config/credentials/development.yml.enc
+++ b/config/credentials/development.yml.enc
@@ -1 +1 @@
-4UEWXd9znHiCoVzWZhiZQQ3ww4nLxno2kP+DIFy9gQWMcewpBPmAvLDx1vOu43SamUGM47wiqCsYpfZvnL+G2XXEAkQbLjZ2Z5GqRrTk2Y9loYfz2BKPPNWYu4zTO99Ok06sOWESXOo7jAWQTJql2uzM4it6V++k1rGYq+ivUXRSPn7O+1d1M/Hd6865SjZgL2g9xCpFMakNnvygcYuYZ31q0WAMPHEvFHFmu5tPvuj4UNtnz8xb60Ij45FDFB0cBpTe6SB7PaNI2f6XLOVqID8qBBYB0ZT1mLwFQUTxfMzhKi5oJ0m9hJ6XzQ3br/BK1ahA4lMJ2OaFne/NM5klYqpvh4U859+aFhAx5/ogEzynC31O5yglUSmVXs2gCMGT11brDT84ElU9VA8jkkci9APdStLN0mTesLIFkMHExHum/Iy0fyKqqb6WLgAaE7SSGYW0YGgmaVYYvXDjvwOfSL0RbaLkkMuiOnI71cq9I5cjlFVcJAo2ZJHxNwvw+vwtmTaYwXGYAlc3d0wpPzoDaKPL9sRzE6zZUIBQwbh+0keIEsKxdSWyE7AFpfTCxdtkBUsbREkXrNTtU6oPsahmmw==--xRBi1g7Uq9Z1xCIP--8xTsLp29V+xfzbjCKRFKhQ==
\ No newline at end of file
+1jXsG5Ciqgbqqb9+NZrJSDx4TkHbLJE7fM4sW/FePGbgYK4rZWnhbCsjU1wCKxm8a7bhP/McJiGVVf/JcZzZYIwARou2DMEsEJpLQhrIJPvplOUjTp52NgW0q3jtjdYdZJigrKIyQNJniEAXaL3WloKedRPlRoLjioZ4ajSyVAo9FwsnLG15Tx9I6kd8+mXCD11yoRLtoBODMI0ZMJS4twKxBjQ0L+TV4/0lXNoAE3eh68ape0gEsfot9v7VxRYjyWHfWH07Ig/rSZyeWmnHYkgXbQGElyMxkSaYTbYHGYz1OVCgGQsgIZ6HoCbZJsLXdrl7IIjTI11PxRsv4sWaGyat37LcOYXh8szQil1BI6y/hdkvfYwc/waAvUqfGXjFmx+pOa1aqwg+zkL5uXSs55YbKz3L9ll7bvzF7ak4edlWBAdBegLaKKrtzMnkGysdCARZkoRUcnYbPjX5YxZTJzU0+yEuzYPEEih9xmYDfvip3+pnW6/IubEa53GQjQ7GhyA+RUHDlhfcKzHZVATLYQhwlxnvQgZ3WxvSRfD+d26mrQFLjHkaTjD7/lGvxi4BVIbIENQ00MTQ0WYVTcEfWHLTeeEIMW1m7DCyk8Vq6esujM+9G1ax9+X3yfIOSC4OFGGem3nEvsAhRFDSLmqYdpKR4Hoyy+MwHVap+upQISw5Vg==--tOwAx02FxcaihWwf--zfFLQvh7hGe2FptPcgSrVA==
\ No newline at end of file
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 75ac041..08043f0 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -59,6 +59,7 @@
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
+ config.active_record.encryption.encrypt_fixtures = true
# Configure Active Record Encryption
config.active_record.encryption.primary_key = "test" * 8
diff --git a/config/initializers/rails_new_config.rb b/config/initializers/rails_new_config.rb
new file mode 100644
index 0000000..afad0e5
--- /dev/null
+++ b/config/initializers/rails_new_config.rb
@@ -0,0 +1,18 @@
+# Centralized configuration for rails new defaults
+module RailsNewConfig
+ mattr_accessor :ruby_version, :rails_version
+
+ # These will be updated when new versions are released
+ self.ruby_version = "3.3.5"
+ self.rails_version = "8.0.0.1"
+
+ class << self
+ def ruby_version_for_new_apps
+ ruby_version
+ end
+
+ def rails_version_for_new_apps
+ rails_version
+ end
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index a0b5cdb..4624c55 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -22,14 +22,12 @@
resources :notifications, only: [ :index, :update ]
- resources :generated_apps, only: [ :show ] do
+ resources :generated_apps, only: [ :show, :create ] do
resources :generation_attempts, only: [ :create ]
resources :log_entries, only: [ :index ]
end
- resources :users, only: [ :show ], path: "" do
- resources :repositories, only: [ :new, :create, :show, :index ]
- end
+ resources :users, only: [ :show ], path: ""
resources :pages, only: :show
@@ -40,5 +38,5 @@
root to: "static#home"
# named routes
- get "/repositories/check_name", to: "repositories#check_name", as: :check_repository_name
+ get "/github/check_name", to: "github#check_name", as: :check_github_name
end
diff --git a/db/migrate/20241215164138_drop_repositories.rb b/db/migrate/20241215164138_drop_repositories.rb
new file mode 100644
index 0000000..ed72cce
--- /dev/null
+++ b/db/migrate/20241215164138_drop_repositories.rb
@@ -0,0 +1,18 @@
+class DropRepositories < ActiveRecord::Migration[8.0]
+ def up
+ drop_table :repositories
+ end
+
+ def down
+ create_table :repositories do |t|
+ t.string :name, null: false
+ t.string :github_url, null: false
+ t.references :user, null: false, foreign_key: true, index: true
+
+ t.timestamps
+
+ t.index :name
+ t.index :github_url, unique: true
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index eda8b35..960f600 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2024_12_12_031732) do
+ActiveRecord::Schema[8.0].define(version: 2024_12_15_164138) do
create_table "_litestream_lock", id: false, force: :cascade do |t|
t.integer "id"
end
@@ -292,17 +292,6 @@
t.index ["created_by_id"], name: "index_recipes_on_created_by_id"
end
- create_table "repositories", force: :cascade do |t|
- t.string "name", null: false
- t.string "github_url", null: false
- t.integer "user_id", null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.index ["github_url"], name: "index_repositories_on_github_url", unique: true
- t.index ["name"], name: "index_repositories_on_name"
- t.index ["user_id"], name: "index_repositories_on_user_id"
- end
-
create_table "solid_queue_blocked_executions", force: :cascade do |t|
t.integer "job_id", null: false
t.string "queue_name", null: false
@@ -466,7 +455,6 @@
add_foreign_key "recipe_ingredients", "ingredients"
add_foreign_key "recipe_ingredients", "recipes"
add_foreign_key "recipes", "users", column: "created_by_id"
- add_foreign_key "repositories", "users"
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
diff --git a/test/components/github_auth_button_component_test.rb b/test/components/github_auth_button_component_test.rb
index c9cf316..72eae93 100644
--- a/test/components/github_auth_button_component_test.rb
+++ b/test/components/github_auth_button_component_test.rb
@@ -13,12 +13,7 @@ class GithubAuthButton::ComponentTest < PhlexComponentTestCase
end
test "renders logout button when user is logged in" do
- user = User.create!(
- name: "Test User",
- provider: "github",
- uid: "123456",
- github_username: "testuser"
- )
+ user = users(:john)
Current.stub(:user, user) do
component = GithubAuthButton::Component.new
diff --git a/test/controllers/generated_apps_controller_test.rb b/test/controllers/generated_apps_controller_test.rb
index c026687..8faed86 100644
--- a/test/controllers/generated_apps_controller_test.rb
+++ b/test/controllers/generated_apps_controller_test.rb
@@ -1,26 +1,86 @@
require "test_helper"
+require_relative "../support/git_test_helper"
class GeneratedAppsControllerTest < ActionDispatch::IntegrationTest
+ include GitTestHelper
+
setup do
@user = users(:jane)
- @generated_app = generated_apps(:blog_app)
- sign_in @user
+ sign_in(@user)
end
test "should show generated app" do
- get generated_app_url(@generated_app)
+ get generated_app_url(generated_apps(:blog_app))
assert_response :success
+ end
+
+ test "requires authentication" do
+ sign_out(@user)
+ post generated_apps_path, params: { app_name: "test-app" }
+ assert_redirected_to root_path
+ assert_equal "Please sign in first.", flash[:alert]
+ end
+
+ test "creates app with valid parameters" do
+ app_name = "my-test-app"
+ api_flag = "--api"
+ database = "--database=mysql"
+
+ Recipe.any_instance.stubs(:commit_changes).returns(true)
+ Recipe.any_instance.stubs(:initial_git_commit).returns(true)
+ GeneratedApp.any_instance.stubs(:commit_changes).returns(true)
+ GeneratedApp.any_instance.stubs(:initial_git_commit).returns(true)
+
+ assert_difference "GeneratedApp.count" do
+ assert_difference "Recipe.count" do
+ post generated_apps_path, params: {
+ app_name: app_name,
+ api_flag: api_flag,
+ database_choice: database
+ }
+ end
+ end
+
+ app = GeneratedApp.last
+ assert_equal app_name, app.name
+ assert_equal @user, app.user
+ assert_equal "#{api_flag} #{database}", app.recipe.cli_flags
- assert_select "h1", @generated_app.name
- assert_select "p", @generated_app.description
- assert_select "p", @generated_app.ruby_version
- assert_select "p", @generated_app.rails_version
- assert_select "a[href=?]", @generated_app.github_repo_url
+ assert_redirected_to generated_app_log_entries_path(app)
end
- test "should not show generated app for unauthorized user" do
- sign_out @user
- get generated_app_url(@generated_app)
- assert_redirected_to root_url
+ test "reuses existing recipe if cli flags match" do
+ recipe = recipes(:api_recipe) # Has "--api --database=postgresql" flags
+
+ GeneratedApp.any_instance.stubs(:commit_changes).returns(true)
+ GeneratedApp.any_instance.stubs(:initial_git_commit).returns(true)
+
+ assert_difference "GeneratedApp.count" do
+ assert_no_difference "Recipe.count" do
+ post generated_apps_path, params: {
+ app_name: "new-api",
+ api_flag: "--api",
+ database_choice: "--database=postgresql"
+ }
+ end
+ end
+
+ app = GeneratedApp.last
+ assert_equal recipe, app.recipe
+ end
+
+ test "starts app generation after creation" do
+ Recipe.any_instance.stubs(:commit_changes).returns(true)
+ Recipe.any_instance.stubs(:initial_git_commit).returns(true)
+ GeneratedApp.any_instance.stubs(:commit_changes).returns(true)
+ GeneratedApp.any_instance.stubs(:initial_git_commit).returns(true)
+
+ AppGeneration::Orchestrator.any_instance.expects(:call)
+
+ post generated_apps_path, params: {
+ app_name: "test-app",
+ api_flag: "--api",
+ database_choice: "--database=mysql"
+ }
end
end
diff --git a/test/controllers/github_controller_test.rb b/test/controllers/github_controller_test.rb
new file mode 100644
index 0000000..7a5b94b
--- /dev/null
+++ b/test/controllers/github_controller_test.rb
@@ -0,0 +1,110 @@
+require "test_helper"
+
+class GithubControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @user = users(:jane)
+ @user.stubs(:github_username).returns("jane_smith")
+ sign_in(@user)
+ end
+
+ test "returns success when repository does not exist" do
+ validator = mock("validator")
+ validator.expects(:repo_exists?).returns(false)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ get check_github_name_path, params: { name: "test-repo" }
+
+ assert_response :success
+ assert_equal({ "available" => true }, response.parsed_body)
+ end
+
+ test "returns success when repository exists" do
+ validator = mock("validator")
+ validator.expects(:repo_exists?).returns(true)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("existing-repo", "jane_smith")
+ .returns(validator)
+
+ get check_github_name_path, params: { name: "existing-repo" }
+
+ assert_response :success
+ assert_equal({ "available" => false }, response.parsed_body)
+ end
+
+ test "handles unauthorized GitHub access" do
+ validator = mock("validator")
+ validator.expects(:repo_exists?).raises(Octokit::Unauthorized)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ get check_github_name_path, params: { name: "test-repo" }
+
+ assert_response :unauthorized
+ assert_equal({ "error" => "GitHub authentication failed" }, response.parsed_body)
+ end
+
+ test "handles forbidden GitHub access" do
+ validator = mock("validator")
+ validator.expects(:repo_exists?).raises(Octokit::Forbidden)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ get check_github_name_path, params: { name: "test-repo" }
+
+ assert_response :unauthorized
+ assert_equal({ "error" => "GitHub authentication failed" }, response.parsed_body)
+ end
+
+ test "handles other GitHub errors" do
+ validator = mock("validator")
+ validator.expects(:repo_exists?).raises(Octokit::Error)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ get check_github_name_path, params: { name: "test-repo" }
+
+ assert_response :unprocessable_entity
+ assert_equal({ "error" => "Could not validate repository name" }, response.parsed_body)
+ end
+
+ test "requires authentication" do
+ sign_out(@user)
+
+ get check_github_name_path, params: { name: "test-repo" }
+
+ assert_response :redirect
+ assert_redirected_to root_path
+ assert_equal "Please sign in first.", flash[:alert]
+ end
+
+ test "logs errors when GitHub authentication fails" do
+ validator = mock("validator")
+ error = Octokit::Unauthorized.new
+ validator.expects(:repo_exists?).raises(error)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ Rails.logger.expects(:error).with("GitHub authentication error: #{error.message}")
+
+ get check_github_name_path, params: { name: "test-repo" }
+ end
+
+ test "logs errors for other GitHub validation failures" do
+ validator = mock("validator")
+ error = Octokit::Error.new
+ validator.expects(:repo_exists?).raises(error)
+ GithubRepositoryNameValidator.expects(:new)
+ .with("test-repo", "jane_smith")
+ .returns(validator)
+
+ Rails.logger.expects(:error).with("GitHub validation error: #{error.message}")
+
+ get check_github_name_path, params: { name: "test-repo" }
+ end
+end
diff --git a/test/controllers/repositories_controller_test.rb b/test/controllers/repositories_controller_test.rb
deleted file mode 100644
index 73fda81..0000000
--- a/test/controllers/repositories_controller_test.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-require "test_helper"
-
-class RepositoriesControllerTest < ActionDispatch::IntegrationTest
- def setup
- @user = users(:john)
- sign_in @user
- end
-
- test "should get index" do
- get user_repositories_path(@user)
- assert_response :success
- end
-
- test "should get new" do
- get new_user_repository_path(@user)
- assert_response :success
- end
-
- test "check_name returns true for valid repository name" do
- validator = mock
- validator.expects(:valid?).returns(true)
- GithubRepositoryNameValidator.expects(:new)
- .with("test-repo", @user.github_username)
- .returns(validator)
-
- get check_repository_name_path, params: { name: "test-repo" }
-
- assert_response :success
- assert_equal({ "available" => true }, JSON.parse(response.body))
- end
-
- test "should create repository" do
- repository = Repository.new(
- name: "test-repo",
- github_url: "https://github.com/johndoe/test-repo",
- user: @user
- )
-
- service_mock = Minitest::Mock.new
- service_mock.expect :create_repository, repository do |name|
- repository.save!
- true
- end
-
- GithubRepositoryService.stub :new, service_mock do
- assert_difference("Repository.count") do
- post user_repositories_path(@user), params: { repository: { name: "test-repo" } }
- end
- end
-
- assert_redirected_to user_repositories_path(@user)
- assert_equal "Repository created successfully!", flash[:notice]
- end
-
- test "should handle repository creation error" do
- service_mock = Minitest::Mock.new
- service_mock.expect :create_repository, nil do |_name|
- raise GithubRepositoryService::Error, "API error"
- end
-
- GithubRepositoryService.stub :new, service_mock do
- post user_repositories_path(@user), params: { repository: { name: "test-repo" } }
- end
-
- assert_redirected_to new_user_repository_path(@user)
- assert_equal "API error", flash[:alert]
- end
-
- test "should redirect to root if not authenticated" do
- sign_out @user
- get user_repositories_path(@user)
- assert_redirected_to root_path
- assert_equal "Please sign in with GitHub first!", flash[:alert]
- end
-end
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
index 80da261..3869fcc 100644
--- a/test/controllers/sessions_controller_test.rb
+++ b/test/controllers/sessions_controller_test.rb
@@ -32,21 +32,22 @@ class SessionControllerTest < ActionDispatch::IntegrationTest
test "successful github sign_in" do
+ user = users(:john)
auth_hash = OmniAuth::AuthHash.new({
provider: "github",
- uid: "123545",
+ uid: user.uid,
info: {
- name: "Test User",
- email: "test@example.com",
- nickname: "testuser",
- image: "http://example.com/image.jpg"
+ name: user.name,
+ email: user.email,
+ nickname: user.github_username,
+ image: user.image
},
credentials: {
token: "mock_token"
},
extra: {
raw_info: {
- login: "testuser"
+ login: user.github_username
}
}
})
@@ -54,16 +55,12 @@ class SessionControllerTest < ActionDispatch::IntegrationTest
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:github] = auth_hash
- # Set up the omniauth.auth environment
get "/auth/github/callback", env: { 'omniauth.auth': auth_hash }
assert_response :redirect
assert_redirected_to dashboard_url
-
- user = User.find_by(email: "test@example.com")
- assert user.present?
- assert_equal session[:user_id], user.id
- assert_equal "Logged in as Test User", flash[:notice]
+ assert_equal user.id, session[:user_id]
+ assert_equal "Logged in as #{user.name}", flash[:notice]
end
test "github oauth failure" do
@@ -135,7 +132,6 @@ class SessionControllerTest < ActionDispatch::IntegrationTest
Recipe.delete_all # Then recipes
Ingredient.delete_all # Then ingredients
Noticed::Notification.delete_all # Then notifications
- Repository.delete_all # Then repositories
User.delete_all # Finally users
silence_omniauth_logger do
diff --git a/test/fixtures/generated_apps.yml b/test/fixtures/generated_apps.yml
index b7bc82d..4b8b6a7 100644
--- a/test/fixtures/generated_apps.yml
+++ b/test/fixtures/generated_apps.yml
@@ -78,7 +78,7 @@ saas_starter:
database: "postgresql"
css: "tailwind"
testing: "minitest"
- source_path: <%= Rails.root.join("tmp", "test_apps", "saas_starter") %>
+ source_path: <%= Rails.root.join("tmp", "test_saas_starter") %>
github_repo_url: "https://github.com/johndoe/saas-starter"
github_repo_name: "saas-starter"
is_public: false
diff --git a/test/fixtures/ingredients.yml b/test/fixtures/ingredients.yml
index ed9ef47..8b9a682 100644
--- a/test/fixtures/ingredients.yml
+++ b/test/fixtures/ingredients.yml
@@ -49,14 +49,14 @@ api_setup:
category: "api"
basic:
- name: "Basic Rails Setup"
- description: "Basic Rails application setup with common configurations"
- template_content: |
- # Basic Rails setup
- gem 'bootsnap'
- gem 'puma'
- configures_with: {}
+ name: "Basic Rails"
+ description: "A basic Rails setup"
+ template_content: "# Basic Rails template"
conflicts_with: []
requires: []
+ configures_with:
+ database:
+ - postgresql
+ - mysql
created_by: john
category: "setup"
diff --git a/test/fixtures/repositories.yml b/test/fixtures/repositories.yml
deleted file mode 100644
index c06b708..0000000
--- a/test/fixtures/repositories.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
-
-# == Schema Information
-#
-# Table name: repositories
-#
-# id :integer not null, primary key
-# github_url :string not null
-# name :string not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# user_id :integer not null
-#
-# Indexes
-#
-# index_repositories_on_github_url (github_url) UNIQUE
-# index_repositories_on_name (name)
-# index_repositories_on_user_id (user_id)
-#
-# Foreign Keys
-#
-# user_id (user_id => users.id)
-#
-one:
- name: repo-one
- github_url: https://github.com/johndoe/repo-one
- user: john
-
-two:
- name: repo-two
- github_url: https://github.com/johndoe/repo-two
- user: john
-
-three:
- name: repo-three
- github_url: https://github.com/jane_smith/repo-three
- user: jane
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index ae82002..3729e65 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -23,25 +23,25 @@
#
john:
- name: John Doe
- email: john@example.com
+ name: "John Doe"
+ email: "john@example.com"
image: https://github.com/images/john.jpg
- provider: github
- uid: "92839283"
+ provider: "github"
+ uid: "123456"
slug: john-doe
- github_username: johndoe
+ github_username: "johndoe"
github_token: "fake-token"
created_at: <%= Time.current %>
updated_at: <%= Time.current %>
jane:
- name: Jane Smith
+ name: "Jane Smith"
email: "jane@example.com"
image: https://github.com/images/jane.jpg
- provider: github
- uid: '789012'
+ provider: "github"
+ uid: "789012"
slug: jane-smith
- github_username: jane_smith
+ github_username: "jane_smith"
github_token: "fake-token"
created_at: <%= Time.current %>
updated_at: <%= Time.current %>
diff --git a/test/models/app_change_test.rb b/test/models/app_change_test.rb
index 15b7e2f..2030c1d 100644
--- a/test/models/app_change_test.rb
+++ b/test/models/app_change_test.rb
@@ -158,6 +158,18 @@ class AppChangeTest < ActiveSupport::TestCase
end
end
+ test "to_git_format includes recipe change type" do
+ app_change = app_changes(:blog_auth_change) # Use existing fixture
+
+ git_format = app_change.to_git_format
+
+ assert_equal app_change.recipe_change.change_type, git_format[:recipe_change_type]
+ assert_equal app_change.configuration, git_format[:configuration]
+ assert_nil git_format[:applied_at], "Expected applied_at to be nil for unapplied change"
+ assert_equal false, git_format[:success], "Expected success to be false for unapplied change"
+ assert_nil git_format[:error_message], "Expected error_message to be nil for unapplied change"
+ end
+
private
def mock_popen3(stdout, stderr, success: true, pid: 12345)
diff --git a/test/models/recipe_test.rb b/test/models/recipe_test.rb
index bea3dfc..fb14d47 100644
--- a/test/models/recipe_test.rb
+++ b/test/models/recipe_test.rb
@@ -186,4 +186,29 @@ class RecipeTest < ActiveSupport::TestCase
assert_equal [ 1, 2 ], @recipe.recipe_ingredients.order(:position).pluck(:position)
end
+
+ test "find_or_create_by_cli_flags! finds existing recipe" do
+ existing = recipes(:api_recipe)
+ recipe = Recipe.find_or_create_by_cli_flags!(existing.cli_flags, @user)
+
+ assert_equal existing, recipe
+ end
+
+ test "find_or_create_by_cli_flags! creates new recipe when none exists" do
+ cli_flags = "--api --minimal"
+
+ Recipe.any_instance.stubs(:commit_changes).returns(true)
+ Recipe.any_instance.stubs(:initial_git_commit).returns(true)
+ GitRepo.any_instance.stubs(:commit_changes).returns(true)
+ GitRepo.any_instance.stubs(:write_model).returns(true)
+
+ assert_difference "Recipe.count", 1 do
+ recipe = Recipe.find_or_create_by_cli_flags!(cli_flags, @user)
+
+ assert_equal cli_flags, recipe.cli_flags
+ assert_equal "Rails App with #{cli_flags}", recipe.name
+ assert_equal "published", recipe.status
+ assert_equal @user, recipe.created_by
+ end
+ end
end
diff --git a/test/models/repository_test.rb b/test/models/repository_test.rb
deleted file mode 100644
index 7132fde..0000000
--- a/test/models/repository_test.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# == Schema Information
-#
-# Table name: repositories
-#
-# id :integer not null, primary key
-# github_url :string not null
-# name :string not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# user_id :integer not null
-#
-# Indexes
-#
-# index_repositories_on_github_url (github_url) UNIQUE
-# index_repositories_on_name (name)
-# index_repositories_on_user_id (user_id)
-#
-# Foreign Keys
-#
-# user_id (user_id => users.id)
-#
-require "test_helper"
-
-class RepositoryTest < ActiveSupport::TestCase
- # test "the truth" do
- # assert true
- # end
-end
diff --git a/test/services/app_generation/orchestrator_test.rb b/test/services/app_generation/orchestrator_test.rb
new file mode 100644
index 0000000..96d3d61
--- /dev/null
+++ b/test/services/app_generation/orchestrator_test.rb
@@ -0,0 +1,56 @@
+require "test_helper"
+require_relative "../../../app/services/app_generation/errors"
+
+module AppGeneration
+ class OrchestratorTest < ActiveSupport::TestCase
+ setup do
+ @generated_app = generated_apps(:pending_app)
+ @orchestrator = Orchestrator.new(@generated_app)
+ end
+
+ test "enqueues generation job when app is in pending state" do
+ assert @generated_app.pending?
+
+ assert_difference -> { SolidQueue::Job.count } do
+ assert @orchestrator.call
+ end
+
+ job = SolidQueue::Job.last
+ assert_equal "AppGenerationJob", job.class_name
+ assert_equal [ @generated_app.id ], job.arguments["arguments"]
+ end
+
+ test "validates app must be in pending state" do
+ @generated_app.app_status.update!(status: "generating")
+ @generated_app.reload
+
+ assert_equal "generating", @generated_app.status
+ assert_not @generated_app.pending?
+
+ error = assert_raises(AppGeneration::Errors::InvalidStateError) do
+ @orchestrator.call
+ end
+
+ assert_equal "App must be in pending state to start generation", error.message
+ end
+
+ test "handles and logs errors during orchestration" do
+ error_message = "Something went wrong"
+ AppGenerationJob.stubs(:perform_later).raises(StandardError.new(error_message))
+
+ # Expect both error logs in sequence
+ sequence = sequence("error_logging")
+ AppGeneration::Logger.any_instance.expects(:error).with(
+ "Failed to start app generation",
+ { error: error_message }
+ ).in_sequence(sequence)
+ AppGeneration::Logger.any_instance.expects(:error).with(
+ "App generation failed: #{error_message}"
+ ).in_sequence(sequence)
+
+ assert_not @orchestrator.call
+ assert @generated_app.reload.failed?
+ assert_equal error_message, @generated_app.app_status.error_message
+ end
+ end
+end
diff --git a/test/services/data_repository_test.rb b/test/services/data_repository_test.rb
new file mode 100644
index 0000000..2f93ae1
--- /dev/null
+++ b/test/services/data_repository_test.rb
@@ -0,0 +1,293 @@
+require "test_helper"
+
+class DataRepositoryTest < ActiveSupport::TestCase
+ fixtures :users, :recipes, :ingredients, :generated_apps
+
+ def setup
+ @user = users(:john)
+ @repo = DataRepository.new(user: @user)
+ @git_mock = mock("git")
+ @repo.stubs(:git).returns(@git_mock)
+ @git_mock.stubs(:fetch)
+ @git_mock.stubs(:reset_hard)
+ @git_mock.stubs(:pull)
+ @git_mock.stubs(:push)
+ end
+
+ # Class method tests
+ def test_name_for_environment_in_development
+ Rails.env.stubs(:development?).returns(true)
+ Rails.env.stubs(:test?).returns(false)
+ Rails.env.stubs(:production?).returns(false)
+
+ assert_equal "rails-new-io-data-dev", DataRepository.name_for_environment
+ end
+
+ def test_name_for_environment_in_test
+ Rails.env.stubs(:development?).returns(false)
+ Rails.env.stubs(:test?).returns(true)
+ Rails.env.stubs(:production?).returns(false)
+
+ assert_equal "rails-new-io-data-test", DataRepository.name_for_environment
+ end
+
+ def test_name_for_environment_in_production
+ Rails.env.stubs(:development?).returns(false)
+ Rails.env.stubs(:test?).returns(false)
+ Rails.env.stubs(:production?).returns(true)
+
+ assert_equal "rails-new-io-data", DataRepository.name_for_environment
+ end
+
+ def test_name_for_environment_raises_error_for_unknown_environment
+ Rails.env.stubs(:development?).returns(false)
+ Rails.env.stubs(:test?).returns(false)
+ Rails.env.stubs(:production?).returns(false)
+
+ assert_raises(ArgumentError) { DataRepository.name_for_environment }
+ end
+
+ # Instance method tests
+ def test_writes_ingredient_correctly
+ ingredient = ingredients(:rails_authentication)
+ base_path = File.join(@repo.send(:repo_path), "ingredients", ingredient.name.parameterize)
+
+ # Expect template.rb write
+ template_path = File.join(base_path, "template.rb")
+ File.expects(:write).with(template_path, ingredient.template_content)
+
+ # Expect metadata.json write
+ metadata_path = File.join(base_path, "metadata.json")
+ metadata_content = {
+ name: ingredient.name,
+ description: ingredient.description,
+ conflicts_with: ingredient.conflicts_with,
+ requires: ingredient.requires,
+ configures_with: ingredient.configures_with
+ }.to_json
+ File.expects(:write).with(metadata_path, metadata_content)
+
+ FileUtils.stubs(:mkdir_p)
+ @repo.stubs(:ensure_fresh_repo)
+ @repo.stubs(:push_to_remote)
+
+ @repo.write_model(ingredient)
+ end
+
+ def test_writes_recipe_correctly
+ recipe = recipes(:blog_recipe)
+ base_path = File.join(@repo.send(:repo_path), "recipes", recipe.id.to_s)
+
+ # Expect manifest.json write
+ manifest_path = File.join(base_path, "manifest.json")
+ manifest_content = {
+ name: recipe.name,
+ cli_flags: recipe.cli_flags,
+ ruby_version: recipe.ruby_version,
+ rails_version: recipe.rails_version
+ }.to_json
+ File.expects(:write).with(manifest_path, manifest_content)
+
+ # Expect ingredients.json write
+ ingredients_path = File.join(base_path, "ingredients.json")
+ ingredients_content = recipe.recipe_ingredients.order(:position).map(&:to_git_format).to_json
+ File.expects(:write).with(ingredients_path, ingredients_content)
+
+ FileUtils.stubs(:mkdir_p)
+ @repo.stubs(:ensure_fresh_repo)
+ @repo.stubs(:push_to_remote)
+
+ @repo.write_model(recipe)
+ end
+
+ def test_raises_error_when_trying_to_write_generated_app
+ app = generated_apps(:blog_app)
+
+ # Mock git branches and status
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git_mock.stubs(:branches).returns(branches_collection)
+
+ # Mock git status
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({})
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git_mock.stubs(:status).returns(status_mock)
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git_mock.stubs(:branch).returns(current_branch_mock)
+
+ # Mock git operations that happen in ensure_fresh_repo
+ @git_mock.stubs(:add).with(all: true)
+ @git_mock.stubs(:commit).with("Initialize repository structure")
+
+ # Stub filesystem operations
+ FileUtils.stubs(:mkdir_p)
+ FileUtils.stubs(:touch)
+ File.stubs(:directory?).returns(false) # This triggers ensure_committable_state
+ File.stubs(:write)
+
+ assert_raises(NotImplementedError, "Generated apps are stored in their own repositories") do
+ @repo.write_model(app)
+ end
+ end
+
+ def test_handles_git_push_error
+ recipe = recipes(:blog_recipe)
+ base_path = File.join(@repo.send(:repo_path), "recipes", recipe.id.to_s)
+
+ # Stub the file writes
+ manifest_path = File.join(base_path, "manifest.json")
+ ingredients_path = File.join(base_path, "ingredients.json")
+ File.stubs(:write).with(manifest_path, anything)
+ File.stubs(:write).with(ingredients_path, anything)
+
+ FileUtils.stubs(:mkdir_p)
+ @repo.stubs(:ensure_fresh_repo)
+ @repo.unstub(:push_to_remote) # We want the real push_to_remote to trigger the error
+ @git_mock.stubs(:push).raises(Git::Error.new("Push failed"))
+
+ assert_raises(GitRepo::GitSyncError) { @repo.write_model(recipe) }
+ end
+
+ def test_ensure_committable_state_creates_required_structure
+ path = @repo.send(:repo_path)
+
+ # Expect directory creation for each required directory
+ %w[ingredients recipes].each do |dir|
+ dir_path = File.join(path, dir)
+ FileUtils.expects(:mkdir_p).with(dir_path)
+ FileUtils.expects(:touch).with(File.join(dir_path, ".keep"))
+ end
+
+ # Expect README.md creation
+ readme_path = File.join(path, "README.md")
+ File.expects(:write).with(readme_path, "# Data Repository\nThis repository contains data for rails-new.io")
+
+ @repo.send(:ensure_committable_state)
+ end
+
+ def test_ensure_fresh_repo_syncs_with_remote
+ sequence = sequence("git_sync")
+
+ # Mock git branches and status
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git_mock.stubs(:branches).returns(branches_collection)
+
+ # Mock git status
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({})
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git_mock.stubs(:status).returns(status_mock)
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git_mock.stubs(:branch).returns(current_branch_mock)
+
+ # Set up sequence of operations
+ @git_mock.expects(:fetch).in_sequence(sequence)
+ @git_mock.expects(:reset_hard).with("origin/main").in_sequence(sequence)
+ @git_mock.expects(:pull).in_sequence(sequence)
+
+ # Mock directory check to avoid initialization
+ File.stubs(:directory?).returns(true)
+
+ @repo.send(:ensure_fresh_repo)
+ end
+
+ def test_repository_description_is_specific_to_data_repo
+ assert_equal "Data repository for rails-new.io", @repo.send(:repository_description)
+ end
+
+ def test_ensure_fresh_repo_commits_and_pushes_when_changes_exist
+ # Mock git branches and status
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git_mock.stubs(:branches).returns(branches_collection)
+
+ # Mock git status to indicate changes
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({ "file1" => "modified" }) # Has changes
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git_mock.stubs(:status).returns(status_mock)
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git_mock.stubs(:branch).returns(current_branch_mock)
+
+ # Mock filesystem operations
+ FileUtils.stubs(:mkdir_p).returns(true) # Return true for all mkdir_p calls
+ FileUtils.stubs(:touch).returns(true) # Return true for all touch calls
+ File.stubs(:directory?).returns(false) # Trigger initialization
+ File.stubs(:write).returns(true) # Allow README.md creation
+
+ # Expect git operations
+ @git_mock.expects(:add).with(all: true)
+ @git_mock.expects(:commit).with("Initialize repository structure")
+ @git_mock.expects(:push).with("origin", "main")
+
+ @repo.send(:ensure_fresh_repo)
+ end
+
+ def test_ensure_fresh_repo_skips_commit_when_no_changes
+ # Mock git branches and status
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git_mock.stubs(:branches).returns(branches_collection)
+
+ # Mock git status to indicate no changes
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({}) # No changes
+ status_mock.stubs(:added).returns({}) # No additions
+ status_mock.stubs(:deleted).returns({})
+ @git_mock.stubs(:status).returns(status_mock)
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git_mock.stubs(:branch).returns(current_branch_mock)
+
+ # Mock filesystem operations
+ FileUtils.stubs(:mkdir_p).returns(true) # Return true for all mkdir_p calls
+ FileUtils.stubs(:touch).returns(true) # Return true for all touch calls
+ File.stubs(:directory?).returns(false) # Trigger initialization
+ File.stubs(:write).returns(true) # Allow README.md creation
+
+ # Expect git operations
+ @git_mock.expects(:add).with(all: true) # add is still called
+ @git_mock.expects(:commit).never # but commit should never happen
+ @git_mock.expects(:push).never # and push should never happen
+
+ @repo.send(:ensure_fresh_repo)
+ end
+
+ private
+
+ def stub_filesystem_operations
+ @repo.stubs(:ensure_fresh_repo)
+ @repo.stubs(:push_to_remote)
+ FileUtils.stubs(:mkdir_p)
+ FileUtils.stubs(:touch)
+ end
+
+ def assert_path_written(relative_path, expected_content)
+ full_path = File.join(@repo.send(:repo_path), relative_path)
+ assert File.stubs(:write).with(full_path, expected_content.to_json).once
+ end
+end
diff --git a/test/services/git_repo_clone_test.rb b/test/services/git_repo_clone_test.rb
new file mode 100644
index 0000000..d246b81
--- /dev/null
+++ b/test/services/git_repo_clone_test.rb
@@ -0,0 +1,76 @@
+require "test_helper"
+
+class GitRepoCloneTest < ActiveSupport::TestCase
+ setup do
+ @user = users(:jane)
+ @user.stubs(:name).returns("Jane Smith")
+ @user.stubs(:email).returns("jane@example.com")
+
+ @repo_name = "rails-new-io-data-test"
+ @repo_path = Rails.root.join("tmp", "git_repos", @user.id.to_s, @repo_name)
+
+ # Stub file operations
+ File.stubs(:write).returns(true)
+ File.stubs(:exist?).returns(true)
+ File.stubs(:read).returns("# Repository\nCreated via railsnew.io")
+ Dir.stubs(:glob).returns([])
+
+ # Initialize Octokit client mock
+ @mock_client = mock("octokit_client")
+ Octokit::Client.stubs(:new).returns(@mock_client)
+
+ # Stub all FileUtils operations globally
+ FileUtils.stubs(:mkdir_p)
+ FileUtils.stubs(:rm_rf)
+
+ @repo = GitRepo.new(user: @user, repo_name: @repo_name)
+ end
+
+ test "clones repository when remote exists but no local copy" do
+ File.stubs(:exist?).with(@repo_path).returns(false)
+ @mock_client.stubs(:repository?).returns(true)
+
+ # Mock git status
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({ "file1" => "modified" })
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+
+ # Mock git branches
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+
+ # Set up cloned git mock with all required stubs
+ cloned_git = mock("cloned_git")
+ cloned_git.stubs(:branch).returns(current_branch_mock)
+ cloned_git.stubs(:branches).returns(branches_collection)
+ cloned_git.stubs(:status).returns(status_mock)
+
+ # Track operations
+ operations = []
+ cloned_git.expects(:config).with("user.name", "Jane Smith").tap { operations << :config_name }
+ cloned_git.expects(:config).with("user.email", "jane@example.com").tap { operations << :config_email }
+ cloned_git.expects(:add).with(all: true).tap { operations << :add }
+ cloned_git.expects(:commit).with("Test commit").tap { operations << :commit }
+ cloned_git.expects(:push).with("origin", "main").tap { operations << :push }
+
+ Git.expects(:clone).with(
+ "https://fake-token@github.com/#{@user.github_username}/#{@repo_name}.git",
+ @repo_name,
+ path: File.dirname(@repo_path)
+ )
+ Git.expects(:open).with(@repo_path).returns(cloned_git)
+
+ @repo.commit_changes(message: "Test commit", author: @user)
+
+ # Assert operations happened in correct order
+ expected_operations = [ :config_name, :config_email, :add, :commit, :push ]
+ assert_equal expected_operations, operations, "Git operations were not performed in the expected order"
+ end
+end
diff --git a/test/services/git_repo_test.rb b/test/services/git_repo_test.rb
index 1e8a616..1c5e2a7 100644
--- a/test/services/git_repo_test.rb
+++ b/test/services/git_repo_test.rb
@@ -3,420 +3,273 @@
class GitRepoTest < ActiveSupport::TestCase
setup do
@user = users(:jane)
- # Define github_token method to bypass encryption
- @user.define_singleton_method(:github_token) { "fake-token" }
+ @user.stubs(:name).returns("Jane Smith")
+ @user.stubs(:email).returns("jane@example.com")
@repo_name = "rails-new-io-data-test"
@repo_path = Rails.root.join("tmp", "git_repos", @user.id.to_s, @repo_name)
- # Ensure parent directory exists and is empty
- FileUtils.rm_rf(File.dirname(@repo_path))
- FileUtils.mkdir_p(File.dirname(@repo_path))
-
# Create a mock git object
@git = mock("git")
@git.stubs(:config)
@git.stubs(:add)
@git.stubs(:commit)
+
+ # Create a branch mock explicitly
+ @branch_mock = mock("branch")
+ @branch_mock.stubs(:name).returns("main")
+ @git.stubs(:branch).returns(@branch_mock)
+
Git.stubs(:init).returns(@git)
Git.stubs(:open).returns(@git)
+ Git.stubs(:clone)
# Stub file operations
- FileUtils.stubs(:mkdir_p).returns(true)
File.stubs(:write).returns(true)
File.stubs(:exist?).returns(true)
- File.stubs(:read).returns("{}")
- JSON.stubs(:parse).returns({})
+ File.stubs(:read).returns("# Repository\nCreated via railsnew.io")
+ Dir.stubs(:glob).returns([])
- # Stub GitHub API calls
- GitRepo.any_instance.stubs(:remote_repo_exists?).returns(false)
- GitRepo.any_instance.stubs(:create_github_repo).returns(true)
- GitRepo.any_instance.stubs(:create_initial_structure).returns(true)
- GitRepo.any_instance.stubs(:setup_remote).returns(true)
+ # Initialize Octokit client mock
+ @mock_client = mock("octokit_client")
+ Octokit::Client.stubs(:new).returns(@mock_client)
+
+ # Stub all FileUtils operations globally
+ FileUtils.stubs(:mkdir_p)
+ FileUtils.stubs(:rm_rf)
@repo = GitRepo.new(user: @user, repo_name: @repo_name)
end
teardown do
- # Clean up after each test
FileUtils.rm_rf(@repo_path) if File.exist?(@repo_path)
FileUtils.rm_rf(File.dirname(@repo_path)) if File.exist?(File.dirname(@repo_path))
-
- # Clean up any remaining stubs
- GitRepo.any_instance.unstub(:remote_repo_exists?)
- GitRepo.any_instance.unstub(:create_github_repo)
- GitRepo.any_instance.unstub(:create_initial_structure)
- GitRepo.any_instance.unstub(:setup_remote)
- Rails.unstub(:env) if Rails.respond_to?(:env) && Rails.env.is_a?(Mocha::Mock)
- end
-
- test "initializes with user and repo name" do
- assert_equal @user, @repo.instance_variable_get(:@user)
- assert_equal @repo_name, @repo.instance_variable_get(:@repo_name)
- expected_path = Rails.root.join("tmp", "git_repos", @user.id.to_s, @repo_name)
- assert_equal expected_path, @repo.instance_variable_get(:@repo_path)
- end
-
- test "writes generated app to repo" do
- app = generated_apps(:blog_app)
- path = File.join(@repo_path, "generated_apps", app.id.to_s)
-
- # Expect git operations
- @git.expects(:fetch)
- @git.expects(:reset_hard).with("origin/main")
- @git.expects(:pull)
- @git.expects(:push).with("origin", "main")
-
- # Expect file operations
- FileUtils.expects(:mkdir_p).with(path)
- File.expects(:write).with(
- File.join(path, "current_state.json"),
- JSON.pretty_generate({
- name: app.name,
- recipe_id: app.recipe_id,
- configuration: app.configuration_options
- })
- )
- File.expects(:write).with(
- File.join(path, "history.json"),
- JSON.pretty_generate(app.app_changes.map(&:to_git_format))
- )
-
- @repo.write_model(app)
- end
-
- test "writes ingredient to repo" do
- ingredient = ingredients(:rails_authentication)
- path = File.join(@repo_path, "ingredients", ingredient.name.parameterize)
-
- # Expect git operations
- @git.expects(:fetch)
- @git.expects(:reset_hard).with("origin/main")
- @git.expects(:pull)
- @git.expects(:push).with("origin", "main")
-
- # Expect file operations
- FileUtils.expects(:mkdir_p).with(path)
- File.expects(:write).with(
- File.join(path, "template.rb"),
- ingredient.template_content
- )
- File.expects(:write).with(
- File.join(path, "metadata.json"),
- JSON.pretty_generate({
- name: ingredient.name,
- description: ingredient.description,
- conflicts_with: ingredient.conflicts_with,
- requires: ingredient.requires,
- configures_with: ingredient.configures_with
- })
- )
-
- @repo.write_model(ingredient)
end
- test "writes recipe to repo" do
- recipe = recipes(:blog_recipe)
- path = File.join(@repo_path, "recipes", recipe.id.to_s)
-
- # Expect git operations
- @git.expects(:fetch)
- @git.expects(:reset_hard).with("origin/main")
- @git.expects(:pull)
- @git.expects(:push).with("origin", "main")
-
- # Expect file operations
- FileUtils.expects(:mkdir_p).with(path)
- File.expects(:write).with(
- File.join(path, "manifest.json"),
- JSON.pretty_generate({
- name: recipe.name,
- cli_flags: recipe.cli_flags,
- ruby_version: recipe.ruby_version,
- rails_version: recipe.rails_version
- })
- )
- File.expects(:write).with(
- File.join(path, "ingredients.json"),
- JSON.pretty_generate(recipe.recipe_ingredients.order(:position).map(&:to_git_format))
- )
-
- @repo.write_model(recipe)
+ test "commits changes to existing local repository" do
+ File.stubs(:exist?).with(@repo_path).returns(true)
+ File.stubs(:exist?).with(File.join(@repo_path, ".git")).returns(true)
+
+ # Create a mock status object
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({ "file1" => "modified" })
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git.stubs(:status).returns(status_mock)
+
+ # Mock branches collection
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git.stubs(:branches).returns(branches_collection)
+
+ # Mock the current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git.stubs(:branch).returns(current_branch_mock)
+
+ # Track the operations performed
+ operations = []
+ @git.expects(:fetch).tap { operations << :fetch }
+ @git.expects(:reset_hard).with("origin/main").tap { operations << :reset }
+ @git.expects(:config).with("user.name", "Jane Smith").tap { operations << :config_name }
+ @git.expects(:config).with("user.email", "jane@example.com").tap { operations << :config_email }
+ @git.expects(:add).with(all: true).tap { operations << :add }
+ @git.expects(:commit).with("Test commit").tap { operations << :commit }
+ @git.expects(:push).with("origin", "main").tap { operations << :push }
+
+ @mock_client.expects(:repository?).with("#{@user.github_username}/#{@repo_name}").returns(true)
+
+ @repo.commit_changes(message: "Test commit", author: @user)
+
+ # Assert all operations were performed in the correct order
+ expected_operations = [ :fetch, :reset, :config_name, :config_email, :add, :commit, :push ]
+ assert_equal expected_operations, operations, "Git operations were not performed in the expected order"
+
+ # Assert repository state
+ assert File.exist?(@repo_path), "Repository directory does not exist"
+ assert File.exist?(File.join(@repo_path, ".git")), "Git directory does not exist"
end
- test "handles git push errors" do
- recipe = recipes(:blog_recipe)
- path = File.join(@repo_path, "recipes", recipe.id.to_s)
-
- # Expect git operations
- @git.expects(:fetch)
- @git.expects(:reset_hard).with("origin/main")
- @git.expects(:pull)
- @git.expects(:push).with("origin", "main").raises(Git::Error.new("push failed"))
-
- # Expect file operations (these need to succeed before the push fails)
- FileUtils.expects(:mkdir_p).with(path)
- File.expects(:write).with(
- File.join(path, "manifest.json"),
- JSON.pretty_generate({
- name: recipe.name,
- cli_flags: recipe.cli_flags,
- ruby_version: recipe.ruby_version,
- rails_version: recipe.rails_version
- })
- )
- File.expects(:write).with(
- File.join(path, "ingredients.json"),
- JSON.pretty_generate(recipe.recipe_ingredients.order(:position).map(&:to_git_format))
- )
-
- assert_raises(GitRepo::GitSyncError) do
- @repo.write_model(recipe)
- end
- end
-
- test "uses correct repo name suffix in development environment" do
- github_client = Minitest::Mock.new
- GitRepo.any_instance.unstub(:remote_repo_exists?)
- GitRepo.any_instance.stubs(:create_github_repo).returns(true)
- GitRepo.any_instance.stubs(:create_initial_structure).returns(true)
- GitRepo.any_instance.stubs(:setup_remote).returns(true)
-
- rails_env = mock
- rails_env.stubs(:development?).returns(true)
- rails_env.stubs(:test?).returns(false)
- rails_env.stubs(:production?).returns(false)
- Rails.stubs(:env).returns(rails_env)
-
- Octokit::Client.stub :new, github_client do
- github_client.expect :repository?, false, [ "#{@user.github_username}/rails-new-io-data-dev" ]
- repo = GitRepo.new(user: @user, repo_name: @repo_name)
- assert_equal "rails-new-io-data-dev", repo.send(:repo_name)
- github_client.verify
- end
- end
-
- test "uses correct repo name suffix in test environment" do
- github_client = Minitest::Mock.new
- GitRepo.any_instance.unstub(:remote_repo_exists?)
- GitRepo.any_instance.stubs(:create_github_repo).returns(true)
- GitRepo.any_instance.stubs(:create_initial_structure).returns(true)
- GitRepo.any_instance.stubs(:setup_remote).returns(true)
-
- rails_env = mock
- rails_env.stubs(:development?).returns(false)
- rails_env.stubs(:test?).returns(true)
- rails_env.stubs(:production?).returns(false)
- rails_env.stubs(:to_s).returns("test")
- Rails.stubs(:env).returns(rails_env)
-
- Octokit::Client.stub :new, github_client do
- github_client.expect :repository?, false, [ "#{@user.github_username}/rails-new-io-data-test" ]
- repo = GitRepo.new(user: @user, repo_name: @repo_name)
- assert_equal "rails-new-io-data-test", repo.send(:repo_name)
- github_client.verify
- end
- end
-
- test "uses base repo name in production environment" do
- # First clear the existing instance to avoid multiple method calls
- @repo = nil
-
- # Remove all existing stubs
- GitRepo.any_instance.unstub(:remote_repo_exists?)
- GitRepo.any_instance.unstub(:create_github_repo)
- GitRepo.any_instance.unstub(:create_initial_structure)
- GitRepo.any_instance.unstub(:setup_remote)
-
- Mocha::Configuration.override(stubbing_non_public_method: :allow) do
- # Set up production environment in a block to ensure cleanup
- rails_env = mock
- rails_env.stubs(:development?).returns(false)
- rails_env.stubs(:test?).returns(false)
- rails_env.stubs(:production?).returns(true)
- Rails.stubs(:env).returns(rails_env)
-
- # Mock GitHub client for this specific test
- github_client = Minitest::Mock.new
- github_client.expect :repository?, false, [ "#{@user.github_username}/rails-new-io-data" ]
-
- # Re-stub necessary methods after the repository check
- GitRepo.any_instance.stubs(:create_github_repo).returns(true)
- GitRepo.any_instance.stubs(:create_initial_structure).returns(true)
- GitRepo.any_instance.stubs(:setup_remote).returns(true)
-
- Octokit::Client.stub :new, github_client do
- repo = GitRepo.new(user: @user, repo_name: @repo_name)
- assert_equal "rails-new-io-data", repo.send(:repo_name)
- github_client.verify
- end
- end
- end
-
- test "raises error for unknown Rails environment" do
- GitRepo.any_instance.unstub(:remote_repo_exists?)
- GitRepo.any_instance.stubs(:create_github_repo).returns(true)
- GitRepo.any_instance.stubs(:create_initial_structure).returns(true)
- GitRepo.any_instance.stubs(:setup_remote).returns(true)
-
- rails_env = mock
- rails_env.stubs(:development?).returns(false)
- rails_env.stubs(:test?).returns(false)
- rails_env.stubs(:production?).returns(false)
- rails_env.stubs(:to_s).returns("staging")
- Rails.stubs(:env).returns(rails_env)
-
- assert_raises(ArgumentError, "Unknown Rails environment: staging") do
- GitRepo.new(user: @user, repo_name: @repo_name)
- end
+ test "creates new local repository when no repository exists" do
+ git_operations = []
+
+ # Clear any existing stubs from setup
+ File.unstub(:exist?)
+
+ # Set up file existence checks
+ File.stubs(:exist?).returns(false) # Default to false for all paths
+ File.stubs(:exist?).with(@repo_path).returns(false)
+ File.stubs(:exist?).with(File.join(@repo_path, ".git")).returns(false)
+
+ # Mock git branches and status (for later use)
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git.stubs(:branches).returns(branches_collection)
+
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({ "file1" => "modified" })
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git.stubs(:status).returns(status_mock)
+
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git.stubs(:branch).returns(current_branch_mock)
+
+ # Set up GitHub API expectations
+ # We expect two checks for remote_repo_exists? - both should return false initially
+ @mock_client.stubs(:repository?)
+ .with("#{@user.github_username}/#{@repo_name}")
+ .returns(false)
+ .then.returns(false)
+ .then.returns(true) # After creation
+
+ @mock_client.expects(:create_repository)
+ .with(@repo_name, private: false, description: "Repository created via railsnew.io")
+ .returns(true)
+
+ # Track git operations
+ @git.expects(:config).with("init.templateDir", "").tap { |_| git_operations << "config_template" }
+ @git.expects(:config).with("init.defaultBranch", "main").tap { |_| git_operations << "config_branch" }
+ @git.expects(:config).with("user.name", "Jane Smith").tap { |_| git_operations << "config_name" }
+ @git.expects(:config).with("user.email", "jane@example.com").tap { |_| git_operations << "config_email" }
+ @git.expects(:add).with(all: true).tap { |_| git_operations << "add" }
+ @git.expects(:commit).with("Test commit").tap { |_| git_operations << "commit" }
+ @git.expects(:add_remote).with(
+ "origin",
+ "https://fake-token@github.com/#{@user.github_username}/#{@repo_name}.git"
+ ).tap { |_| git_operations << "add_remote" }
+ @git.expects(:push).with("origin", "main").tap { |_| git_operations << "push" }
+
+
+
+ @repo.commit_changes(message: "Test commit", author: @user)
+
+ # Assert operations happened in correct order
+ expected_operations = [
+ "config_template",
+ "config_branch",
+ "config_name",
+ "config_email",
+ "add",
+ "commit",
+ "add_remote",
+ "push"
+ ]
+ assert_equal expected_operations, git_operations, "Git operations were not performed in the expected order"
end
test "handles GitHub API errors when checking repository existence" do
- # Clear the stub for remote_repo_exists? since we want to test it
- GitRepo.any_instance.unstub(:remote_repo_exists?)
-
- # Create a test logger
- test_logger = Class.new do
- attr_reader :messages
- def initialize
- @messages = []
- end
-
- def error(message)
- @messages << message
- end
- end.new
-
- Rails.stubs(:logger).returns(test_logger)
-
- # Create a client that raises an error
error = Octokit::Error.new(
method: :get,
- url: "https://api.github.com/repos/#{@user.github_username}/rails-new-io-data-test",
+ url: "https://api.github.com/repos/#{@user.github_username}/#{@repo_name}",
status: 401,
response_headers: {},
body: { message: "Bad credentials" }
)
- mock_client = mock
- mock_client.expects(:repository?).raises(error)
+ test_logger = mock("logger")
+ test_logger.expects(:error).with("Failed to check GitHub repository: #{error.message}")
+ Rails.stubs(:logger).returns(test_logger)
- # Test error handling
- @repo.instance_variable_set(:@github_client, mock_client)
- result = @repo.send(:remote_repo_exists?)
+ @mock_client.expects(:repository?).raises(error)
- # Verify behavior
- assert_equal false, result
- assert_equal 1, test_logger.messages.size
- assert_equal(
- "Failed to check GitHub repository: GET https://api.github.com/repos/jane_smith/rails-new-io-data-test: 401 - Bad credentials",
- test_logger.messages.first
- )
+ # Should return false and continue with local repo creation
+ @repo.send(:remote_repo_exists?)
end
-end
-
-class GitRepoCreateTest < ActiveSupport::TestCase
- test "creates github repository with correct parameters" do
- # Create a test user
- test_user = User.new(github_username: "test_user", github_token: "fake-token")
-
- # Create a repo instance WITHOUT initialization
- repo = Class.new(GitRepo) do
- def initialize(user:)
- @user = user
- end
- end.new(user: test_user)
-
- # Set up the test expectations
- mock_client = mock("github_client")
- mock_client.expects(:create_repository).with(
- "rails-new-io-data-test",
- private: false,
- description: "Data repository for rails-new.io"
- ).returns(true)
- # Inject our dependencies
- repo.stubs(:repo_name).returns("rails-new-io-data-test")
- repo.instance_variable_set(:@github_client, mock_client)
-
- # Test just the create_github_repo method
- repo.send(:create_github_repo)
+ test "handles missing user name and email" do
+ @user.stubs(:name).returns(nil)
+ @user.stubs(:email).returns(nil)
+
+ File.stubs(:exist?).with(@repo_path).returns(true)
+ File.stubs(:exist?).with(File.join(@repo_path, ".git")).returns(true)
+
+ # Mock git branches and status
+ remote_branch = mock("remote_branch")
+ remote_branch.stubs(:name).returns("origin/main")
+ branches_collection = mock("branches_collection")
+ branches_collection.stubs(:remote).returns([ remote_branch ])
+ @git.stubs(:branches).returns(branches_collection)
+
+ # Mock git status
+ status_mock = mock("status")
+ status_mock.stubs(:changed).returns({ "file1" => "modified" })
+ status_mock.stubs(:added).returns({})
+ status_mock.stubs(:deleted).returns({})
+ @git.stubs(:status).returns(status_mock)
+
+ # Mock current branch
+ current_branch_mock = mock("current_branch")
+ current_branch_mock.stubs(:name).returns("main")
+ @git.stubs(:branch).returns(current_branch_mock)
+
+ # Track operations
+ operations = []
+ @git.expects(:fetch).tap { operations << :fetch }
+ @git.expects(:reset_hard).with("origin/main").tap { operations << :reset }
+ @git.expects(:config).with("user.name", @user.github_username).tap { operations << :config_name }
+ @git.expects(:config).with("user.email", "#{@user.github_username}@users.noreply.github.com").tap { operations << :config_email }
+ @git.expects(:add).with(all: true).tap { operations << :add }
+ @git.expects(:commit).with("Test commit").tap { operations << :commit }
+ @git.expects(:push).with("origin", "main").tap { operations << :push }
+
+ @mock_client.expects(:repository?).returns(true)
+
+ @repo.commit_changes(message: "Test commit", author: @user)
+
+ # Assert operations happened in correct order
+ expected_operations = [ :fetch, :reset, :config_name, :config_email, :add, :commit, :push ]
+ assert_equal expected_operations, operations, "Git operations were not performed in the expected order"
end
-end
-class GitRepoStructureTest < ActiveSupport::TestCase
- setup do
- @user = users(:jane)
- @user.define_singleton_method(:github_token) { "fake-token" }
- @repo_name = "rails-new-io-data-test"
- @repo_path = Rails.root.join("tmp", "git_repos", @user.id.to_s, @repo_name)
- end
+ test "ensures committable state by creating README.md" do
+ File.stubs(:exist?).with(@repo_path).returns(true)
+ File.stubs(:exist?).with(File.join(@repo_path, ".git")).returns(true)
- test "creates initial repository structure" do
- # Create a minimal repo instance with readme_content method
- repo = Class.new(GitRepo) do
- def initialize(user:, repo_path:)
- @user = user
- @repo_path = repo_path
- end
-
- private
-
- def readme_content
- "# Data Repository\nThis repository contains data for rails-new.io"
- end
- end.new(user: @user, repo_path: @repo_path)
-
- # Mock Git operations
- git = mock("git")
- git.expects(:add).with(all: true)
- git.expects(:commit).with("Initial commit")
- repo.stubs(:git).returns(git)
-
- # Mock file operations
- FileUtils.expects(:mkdir_p).with(File.join(@repo_path, "generated_apps"))
- FileUtils.expects(:mkdir_p).with(File.join(@repo_path, "ingredients"))
- FileUtils.expects(:mkdir_p).with(File.join(@repo_path, "recipes"))
File.expects(:write).with(
File.join(@repo_path, "README.md"),
- "# Data Repository\nThis repository contains data for rails-new.io"
+ "# Repository\nCreated via railsnew.io"
)
- # Test just the create_initial_structure method
- repo.send(:create_initial_structure)
+ @repo.send(:ensure_committable_state)
end
-end
-class GitRepoRemoteTest < ActiveSupport::TestCase
- setup do
- @user = users(:jane)
- @user.define_singleton_method(:github_token) { "fake-token" }
- @repo_name = "rails-new-io-data-test"
- @repo_path = Rails.root.join("tmp", "git_repos", @user.id.to_s, @repo_name)
+ test "creates GitHub repository with correct parameters" do
+ @mock_client.expects(:create_repository).with(
+ @repo_name,
+ private: false,
+ description: "Repository created via railsnew.io"
+ )
+
+ @repo.send(:create_github_repo)
end
- test "sets up git remote with correct URL" do
- # Create a minimal repo instance
- repo = Class.new(GitRepo) do
- def initialize(user:, repo_path:)
- @user = user
- @repo_path = repo_path
- end
-
- def repo_name
- "rails-new-io-data-test"
- end
- end.new(user: @user, repo_path: @repo_path)
-
- # Mock Git operations
- git = mock("git")
- git.expects(:add_remote).with(
- "origin",
- "https://fake-token@github.com/#{@user.github_username}/rails-new-io-data-test.git"
- )
- repo.stubs(:git).returns(git)
+ test "initializes new git repo when .git directory doesn't exist" do
+ # Stub File.exist? to return false for .git directory
+ File.stubs(:exist?).returns(true) # default stub
+ File.stubs(:exist?).with(File.join(@repo_path, ".git")).returns(false)
+
+ # Set up expectations for create_local_repo
+ FileUtils.expects(:mkdir_p).with(File.dirname(@repo_path))
+ FileUtils.expects(:rm_rf).with(@repo_path)
+ FileUtils.expects(:mkdir_p).with(@repo_path)
+
+ # Expect Git.init to be called and return our mock git object
+ Git.expects(:init).with(@repo_path).returns(@git)
+
+ # Expect git config calls
+ @git.expects(:config).with("init.templateDir", "")
+ @git.expects(:config).with("init.defaultBranch", "main")
+
+ # Call the git method
+ result = @repo.send(:git)
- # Test the setup_remote method
- repo.send(:setup_remote)
+ # Verify the result
+ assert_equal @git, result
end
end
diff --git a/test/services/github_code_push_service_test.rb b/test/services/github_code_push_service_test.rb
index 4a5be34..28608ba 100644
--- a/test/services/github_code_push_service_test.rb
+++ b/test/services/github_code_push_service_test.rb
@@ -3,6 +3,7 @@
class GithubCodePushServiceTest < ActiveSupport::TestCase
def setup
@user = users(:john)
+ @user.stubs(:github_token).returns("fake-token")
@recipe = recipes(:blog_recipe)
@temp_dir = Dir.mktmpdir
@@ -159,7 +160,7 @@ def teardown
test "push_code raises GitError when git operations fail" do
@generated_app = generated_apps(:saas_starter)
@user = @generated_app.user
- @user.define_singleton_method(:github_token) { "fake-token" }
+ @user.stubs(:github_token).returns("fake-token")
# Create directory structure but don't init git
app_dir = File.join(@temp_dir, @generated_app.name)
diff --git a/test/services/github_repository_name_validator_test.rb b/test/services/github_repository_name_validator_test.rb
index 5e0494a..c4f63fc 100644
--- a/test/services/github_repository_name_validator_test.rb
+++ b/test/services/github_repository_name_validator_test.rb
@@ -5,60 +5,88 @@ def setup
@owner = "test-owner"
end
- def test_valid_repository_name
+ def test_available_repository_name
client = Minitest::Mock.new
- client.expect(:repository, nil) { raise Octokit::NotFound }
+ client.expect(:repository?, false, [ "#{@owner}/valid-repo-name" ])
Octokit::Client.stub(:new, client) do
validator = GithubRepositoryNameValidator.new("valid-repo-name", @owner)
- assert validator.valid?
+ assert_not validator.repo_exists?
end
end
def test_invalid_ending_with_hyphen
validator = GithubRepositoryNameValidator.new("invalid-", @owner)
- assert_not validator.valid?
+ assert_not validator.repo_exists?
end
def test_invalid_starting_with_hyphen
validator = GithubRepositoryNameValidator.new("-invalid", @owner)
- assert_not validator.valid?
+ assert_not validator.repo_exists?
end
def test_invalid_double_hyphen
validator = GithubRepositoryNameValidator.new("invalid--name", @owner)
- assert_not validator.valid?
+ assert_not validator.repo_exists?
end
def test_invalid_empty_string
validator = GithubRepositoryNameValidator.new("", @owner)
- assert_not validator.valid?
+ assert_not validator.repo_exists?
end
def test_invalid_special_characters
validator = GithubRepositoryNameValidator.new("inv@lid", @owner)
- assert_not validator.valid?
+ assert_not validator.repo_exists?
end
def test_invalid_when_repository_exists
client = Minitest::Mock.new
- client.expect(:repository, true, [ "#{@owner}/existing-repo" ])
+ client.expect(:repository?, true, [ "#{@owner}/existing-repo" ])
Octokit::Client.stub(:new, client) do
validator = GithubRepositoryNameValidator.new("existing-repo", @owner)
- assert_not validator.valid?
+ assert validator.repo_exists?
end
assert_mock client
end
def test_invalid_with_nil_name
+ validator = GithubRepositoryNameValidator.new(nil, @owner)
+ assert_not validator.repo_exists?
+ end
+
+ def test_handles_github_api_errors
+ error = Octokit::Error.new(
+ method: :get,
+ url: "https://api.github.com/repos/#{@owner}/test-repo",
+ status: 401,
+ response_headers: {},
+ body: { message: "Bad credentials" }
+ )
+
+ # Set up logger mock
+ mock_logger = mock("logger")
+ mock_logger.expects(:error).with(
+ "GitHub API error: GET https://api.github.com/repos/#{@owner}/test-repo: 401 - Bad credentials"
+ )
+ Rails.stubs(:logger).returns(mock_logger)
+
+ # Set up client mock to raise error
client = Minitest::Mock.new
- client.expect(:repository, nil) { raise Octokit::NotFound }
+ client.expect(:repository?, nil) { raise error }
Octokit::Client.stub(:new, client) do
- validator = GithubRepositoryNameValidator.new(nil, @owner)
- assert_not validator.valid?
+ validator = GithubRepositoryNameValidator.new("test-repo", @owner)
+
+ error = assert_raises(Octokit::Error) do
+ validator.repo_exists?
+ end
+
+ assert_equal "GET https://api.github.com/repos/#{@owner}/test-repo: 401 - Bad credentials", error.message
end
+
+ assert_mock client
end
end
diff --git a/test/services/github_repository_service_test.rb b/test/services/github_repository_service_test.rb
index 6a96e75..72f1cd7 100644
--- a/test/services/github_repository_service_test.rb
+++ b/test/services/github_repository_service_test.rb
@@ -5,8 +5,7 @@
class GithubRepositoryServiceTest < ActiveSupport::TestCase
def setup
@user = users(:john)
- # Define github_token method to bypass encryption
- @user.define_singleton_method(:github_token) { "fake-token" }
+ @user.stubs(:github_token).returns("fake-token")
@generated_app = generated_apps(:pending_app)
@app_status = @generated_app.app_status
@@ -33,21 +32,17 @@ def setup
description: "Repository created via railsnew.io"
} ]
- @service.stub :client, mock_client do
- assert_difference -> { @user.repositories.count }, 1 do
- assert_difference -> { AppGeneration::LogEntry.count }, 3 do
- result = @service.create_repository(@repository_name)
- assert_equal response.html_url, result.html_url
- end
- end
+ Octokit::Client.stub :new, mock_client do
+ response = @service.create_repository(@repository_name)
+ assert_equal "https://github.com/#{@user.github_username}/#{@repository_name}", response.html_url
- # Verify log entries
- log_entries = @generated_app.log_entries.recent_first
- assert_equal "GitHub repo #{@repository_name} created successfully", log_entries.first.message
- assert_equal "Creating repository: #{@repository_name}", log_entries.second.message
+ # Verify GeneratedApp was updated
+ @generated_app.reload
+ assert_equal @repository_name, @generated_app.github_repo_name
+ assert_equal response.html_url, @generated_app.github_repo_url
end
- mock_client.verify
+ assert_mock mock_client
end
test "raises error when repository already exists" do
@@ -173,25 +168,4 @@ def mock_client.repository?(*)
end
end
end
-
- test "client initializes Octokit::Client with correct parameters" do
- User.any_instance.stubs(:github_token).returns("fake-token")
-
- mock_client = mock("client")
- Octokit::Client.expects(:new).with(
- access_token: "fake-token",
- auto_paginate: true
- ).returns(mock_client)
-
- @service.send(:client)
- end
-
- test "client memoizes the instance" do
- User.any_instance.stubs(:github_token).returns("fake-token")
-
- first_client = @service.send(:client)
- second_client = @service.send(:client)
-
- assert_same first_client, second_client
- end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 0b913ce..74a7e8d 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -16,7 +16,7 @@
add_group "Jobs", "app/jobs"
add_group "Mailers", "app/mailers"
- track_files "{app/models,app/controllers,app/helpers}/**/*.rb"
+ track_files "{app/models,app/controllers,app/helpers,app/jobs}/**/*.rb"
end
end