コミットを比較
2
コミット
main
...
feature/422
| 作成者 | SHA1 | 日付 | |
|---|---|---|---|
|
|
bf0a2977fc | ||
|
|
11eedc242d |
@@ -2,21 +2,129 @@ class WikiPagesController < ApplicationController
|
||||
rescue_from Wiki::Commit::Conflict, with: :render_wiki_conflict
|
||||
|
||||
def index
|
||||
# 互換性のための化石リソース
|
||||
# TODO: ヘルプおよびタグ・グループ追加後に,
|
||||
# TODO: タグ説明とヘルプ、タグ・グループから取得する作りに変更する.
|
||||
|
||||
title = params[:title].to_s.strip
|
||||
if title.blank?
|
||||
return render json: WikiPageRepr.base(
|
||||
WikiPage.joins(:tag_name).includes(tag_name: :tag))
|
||||
tag_descriptions = TagDescription.where(language_code: :ja).map do |td|
|
||||
{ id: td.id,
|
||||
tag_name_id: td.tag.tag_name(:ja).id,
|
||||
body: td.body,
|
||||
created_user_id: td.created_by_user_id,
|
||||
updated_user_id: td.updated_by_user_id,
|
||||
created_at: td.created_at,
|
||||
updated_at: td.updated_at,
|
||||
discarded_at: nil,
|
||||
version_no: td.version_no,
|
||||
title: td.tag.name(:ja),
|
||||
deprecated_at: td.tag.deprecated_at }
|
||||
end
|
||||
# TODO: ヘルプ移行したらここに追記
|
||||
# TODO: タグ・グループ追加したらここに追記
|
||||
|
||||
# TODO: ヘルプ、タグ・グループ移行したらそれらもタイトルから除外に含める.
|
||||
# てかこれ何順?
|
||||
wiki_pages =
|
||||
WikiPageRepr.base(WikiPage.joins(:tag_name).includes(tag_name: :tag))
|
||||
.reject { |wp| tag_descriptions.any? { |td| td.title == wp.title } }
|
||||
|
||||
json = tag_descriptions + wiki_pages
|
||||
|
||||
return render json:
|
||||
end
|
||||
|
||||
q = WikiPage.joins(:tag_name).includes(tag_name: :tag)
|
||||
q =
|
||||
TagDescription.joins(tag: :tag_names)
|
||||
.where(language_code: :ja)
|
||||
.where(tag: { tag_names: { language_code: :ja, primary_flg: true } })
|
||||
.where('tag_names.name LIKE ?', "%#{ ApplicationRecord.sanitize_sql_like(title) }%")
|
||||
tag_descriptions = q.limit(20).map do |td|
|
||||
{ id: td.id,
|
||||
tag_name_id: td.tag.tag_name(:ja).id,
|
||||
body: td.body,
|
||||
created_user_id: td.created_by_user_id,
|
||||
updated_user_id: td.updated_by_user_id,
|
||||
created_at: td.created_at,
|
||||
updated_at: td.updated_at,
|
||||
discarded_at: nil,
|
||||
version_no: td.version_no,
|
||||
title: td.tag.name(:ja),
|
||||
deprecated_at: td.tag.deprecated_at }
|
||||
end
|
||||
|
||||
q =
|
||||
WikiPage.joins(:tag_name).includes(tag_name: :tag)
|
||||
.where('tag_names.name LIKE ?', "%#{ WikiPage.sanitize_sql_like(title) }%")
|
||||
render json: WikiPageRepr.base(q.limit(20))
|
||||
# TODO: ヘルプ、タグ・グループ移行したらそれらもタイトルから除外に含める.
|
||||
wiki_pages =
|
||||
WikiPageRepr.base(q.limit(20))
|
||||
.reject { |wp| tag_descriptions.any? { |td| td.title == wp.title } }
|
||||
|
||||
json = (tag_descriptions + wiki_pages)[...20]
|
||||
|
||||
render json:
|
||||
end
|
||||
|
||||
def show
|
||||
tag_description = TagDescription.find_by(id: params[:id])
|
||||
|
||||
if tag_description
|
||||
rev =
|
||||
if params[:version].present?
|
||||
TagDescriptionVersion.find_by(id: params[:version])
|
||||
else
|
||||
TagDescriptionVersion.find_by(tag_id: tag_description.tag_id,
|
||||
language_code: :ja,
|
||||
version_no: tag_description.version_no)
|
||||
end
|
||||
|
||||
if rev
|
||||
body =
|
||||
if rev.version_no == tag_description.version_no
|
||||
tag_description.body
|
||||
else
|
||||
rev.body
|
||||
end
|
||||
revision_id = rev.id
|
||||
pred = TagDescriptionVersion.find_by(tag_id: tag_description.tag_id,
|
||||
language_code: :ja,
|
||||
version_no: tag_description.version_no - 1)&.id
|
||||
succ = TagDescriptionVersion.find_by(tag_id: tag_description.tag_id,
|
||||
language_code: :ja,
|
||||
version_no: tag_description.version_no + 1)&.id
|
||||
updated_at =
|
||||
if rev.version_no == tag_description.version_no
|
||||
tag_description.updated_at
|
||||
else
|
||||
rev.created_at
|
||||
end
|
||||
|
||||
json =
|
||||
{ id: tag_description.id,
|
||||
tag_name_id: tag_description.tag.tag_name(:ja).id,
|
||||
body:,
|
||||
created_user_id: tag_description.created_by_user_id,
|
||||
updated_user_id: tag_description.updated_by_user_id,
|
||||
created_at: tag_description.created_at,
|
||||
updated_at:,
|
||||
discarded_at: nil,
|
||||
version_no: tag_description.version_no,
|
||||
title: tag_description.tag.name(:ja),
|
||||
deprecated_at: tag_description.tag.deprecated_at,
|
||||
revision_id:,
|
||||
pred:,
|
||||
succ: }
|
||||
|
||||
return render json:
|
||||
end
|
||||
end
|
||||
|
||||
page = WikiPage.joins(:tag_name)
|
||||
.includes(tag_name: :tag)
|
||||
.find_by(id: params[:id])
|
||||
|
||||
render_wiki_page_or_404 page
|
||||
end
|
||||
|
||||
@@ -156,10 +264,6 @@ class WikiPagesController < ApplicationController
|
||||
head :ok
|
||||
end
|
||||
|
||||
def search
|
||||
index
|
||||
end
|
||||
|
||||
def changes
|
||||
id = params[:id].presence
|
||||
q = WikiRevision.joins(wiki_page: :tag_name)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class TagDescription < ApplicationRecord
|
||||
belongs_to :tag
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
class TagDescriptionVersion < ApplicationRecord
|
||||
;
|
||||
end
|
||||
@@ -48,8 +48,11 @@ class WikiPage < ApplicationRecord
|
||||
page
|
||||
end
|
||||
|
||||
def pred_revision_id(revision_id) =
|
||||
wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id)
|
||||
def succ_revision_id(revision_id) =
|
||||
wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id)
|
||||
def pred_revision_id revision_id
|
||||
wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id)
|
||||
end
|
||||
|
||||
def succ_revision_id revision_id
|
||||
wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :wiki_pages, path: 'wiki', only: [:index, :show, :create, :update] do
|
||||
collection do
|
||||
get :search
|
||||
get :search, to: redirect('/')
|
||||
get :changes
|
||||
|
||||
scope :title do
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
class CreateTagDescriptions < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :tag_description_versions do |t|
|
||||
t.references :tag, null: false, foreign_key: false
|
||||
t.string :language_code, limit: 16, null: false
|
||||
t.integer :version_no, null: false
|
||||
t.string :event_type, null: false
|
||||
t.text :body, null: false
|
||||
t.string :source_language_code, limit: 16
|
||||
t.integer :source_version_no
|
||||
t.references :created_by_user
|
||||
t.datetime :created_at, null: false
|
||||
|
||||
t.index [:tag_id, :language_code, :version_no], unique: true
|
||||
t.foreign_key :tag_description_versions,
|
||||
column: [:tag_id, :source_language_code, :source_version_no],
|
||||
primary_key: [:tag_id, :language_code, :version_no]
|
||||
|
||||
t.check_constraint 'version_no > 0',
|
||||
name: 'chk_tag_description_versions_version_no_positive'
|
||||
|
||||
t.check_constraint "event_type IN ('create', 'update', 'discard', 'restore')",
|
||||
name: 'tag_description_versions_event_type_valid'
|
||||
|
||||
t.check_constraint ('(source_language_code IS NULL AND source_version_no IS NULL) OR ' +
|
||||
'(source_language_code IS NOT NULL AND source_version_no IS NOT NULL)'),
|
||||
name: 'chk_tag_description_versions_source_pair'
|
||||
|
||||
t.check_constraint 'source_language_code IS NULL OR source_language_code <> language_code',
|
||||
name: 'chk_tag_description_versions_source_language_code_different'
|
||||
end
|
||||
|
||||
create_table :tag_descriptions do |t|
|
||||
t.references :tag, null: false, foreign_key: true
|
||||
t.string :language_code, limit: 16, null: false, index: true
|
||||
t.integer :version_no, null: false
|
||||
t.text :body, null: false
|
||||
t.string :source_language_code, limit: 16
|
||||
t.integer :source_version_no
|
||||
t.references :created_by_user, foreign_key: { to_table: :users }
|
||||
t.references :updated_by_user, foreign_key: { to_table: :users }
|
||||
t.timestamps
|
||||
|
||||
t.foreign_key :languages, column: :language_code, primary_key: :code
|
||||
t.foreign_key :tag_description_versions,
|
||||
column: [:tag_id, :source_language_code, :source_version_no],
|
||||
primary_key: [:tag_id, :language_code, :version_no]
|
||||
|
||||
t.index [:tag_id, :language_code], unique: true
|
||||
t.index [:source_language_code, :source_version_no]
|
||||
|
||||
t.check_constraint 'version_no > 0',
|
||||
name: 'chk_tag_descriptions_version_no_positive'
|
||||
|
||||
t.check_constraint ('(source_language_code IS NULL AND source_version_no IS NULL) OR ' +
|
||||
'(source_language_code IS NOT NULL AND source_version_no IS NOT NULL)'),
|
||||
name: 'chk_tag_descriptions_source_pair'
|
||||
|
||||
t.check_constraint 'source_language_code IS NULL OR source_language_code <> language_code',
|
||||
name: 'chk_tag_descriptions_source_language_code_different'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
class MigrateTagDescriptionsFromWikiPages < ActiveRecord::Migration[8.0]
|
||||
class WikiPage < ActiveRecord::Base
|
||||
self.table_name = 'wiki_pages'
|
||||
end
|
||||
|
||||
class WikiRevision < ActiveRecord::Base
|
||||
self.table_name = 'wiki_revisions'
|
||||
end
|
||||
|
||||
class WikiRevisionLine < ActiveRecord::Base
|
||||
self.table_name = 'wiki_revision_lines'
|
||||
end
|
||||
|
||||
class TagName < ActiveRecord::Base
|
||||
self.table_name = 'tag_names'
|
||||
end
|
||||
|
||||
class TagDescription < ActiveRecord::Base
|
||||
self.table_name = 'tag_descriptions'
|
||||
end
|
||||
|
||||
class TagDescriptionVersion < ActiveRecord::Base
|
||||
self.table_name = 'tag_description_versions'
|
||||
end
|
||||
|
||||
def up
|
||||
WikiPage.find_each do |page|
|
||||
tag_name = TagName.find_by(id: page.tag_name_id, language_code: 'ja')
|
||||
next unless tag_name&.tag_id
|
||||
|
||||
revisions = WikiRevision.where(wiki_page_id: page.id).order(:id).to_a
|
||||
|
||||
revisions.each_with_index do |revision, index|
|
||||
version_no = index + 1
|
||||
|
||||
tag_id = tag_name[:tag_id]
|
||||
|
||||
unless tag_id
|
||||
raise (
|
||||
'tag_id is nil: ' +
|
||||
"wiki_page_id=#{ page.id }, " +
|
||||
"tag_name_id=#{ tag_name.id }, " +
|
||||
"tag_name=#{ tag_name.attributes.inspect}")
|
||||
end
|
||||
|
||||
revision_id = revision.id
|
||||
|
||||
say (
|
||||
"wiki_page_id=#{ page.id }, " +
|
||||
"tag_name_id=#{ tag_name.id }, " +
|
||||
"tag_id=#{ tag_id }, " +
|
||||
"revision_id=#{ revision_id }")
|
||||
|
||||
version = TagDescriptionVersion.new(
|
||||
id: revision_id,
|
||||
tag_id:,
|
||||
language_code: 'ja',
|
||||
version_no:,
|
||||
event_type: version_no == 1 ? 'create' : 'update',
|
||||
body: revision_body(revision_id),
|
||||
source_language_code: nil,
|
||||
source_version_no: nil,
|
||||
created_by_user_id: revision.created_user_id,
|
||||
created_at: revision.created_at)
|
||||
|
||||
say version.attributes.inspect
|
||||
|
||||
version.save!
|
||||
end
|
||||
|
||||
next if page.discarded_at.present?
|
||||
next if revisions.empty?
|
||||
|
||||
TagDescription.create!(
|
||||
id: page.id,
|
||||
tag_id: tag_name.tag_id,
|
||||
language_code: 'ja',
|
||||
version_no: revisions.length,
|
||||
body: page.body,
|
||||
source_language_code: nil,
|
||||
source_version_no: nil,
|
||||
created_by_user_id: page.created_user_id,
|
||||
updated_by_user_id: page.updated_user_id,
|
||||
created_at: page.created_at,
|
||||
updated_at: page.updated_at)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
raise ActiveRecord::IrreversibleMigration, '戻せません.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def revision_body revision_id
|
||||
WikiRevisionLine
|
||||
.joins('INNER JOIN wiki_lines ' +
|
||||
'ON wiki_lines.id = wiki_revision_lines.wiki_line_id')
|
||||
.where(wiki_revision_id: revision_id)
|
||||
.order(:position)
|
||||
.pluck('wiki_lines.body')
|
||||
.join("\n")
|
||||
end
|
||||
end
|
||||
生成ファイル
+50
-17
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_10_03_010000) do
|
||||
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "record_type", null: false
|
||||
@@ -440,6 +440,49 @@ ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
t.index ["user_id"], name: "index_settings_on_user_id", unique: true
|
||||
end
|
||||
|
||||
create_table "tag_description_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "tag_id", null: false
|
||||
t.string "language_code", limit: 16, null: false
|
||||
t.integer "version_no", null: false
|
||||
t.string "event_type", null: false
|
||||
t.text "body", null: false
|
||||
t.string "source_language_code", limit: 16
|
||||
t.integer "source_version_no"
|
||||
t.bigint "created_by_user_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["created_by_user_id"], name: "index_tag_description_versions_on_created_by_user_id"
|
||||
t.index ["tag_id", "language_code", "version_no"], name: "idx_on_tag_id_language_code_version_no_ced77e57e6", unique: true
|
||||
t.index ["tag_id", "source_language_code", "source_version_no"], name: "fk_rails_4f13094dbc"
|
||||
t.index ["tag_id"], name: "index_tag_description_versions_on_tag_id"
|
||||
t.check_constraint "((`source_language_code` is null) and (`source_version_no` is null)) or ((`source_language_code` is not null) and (`source_version_no` is not null))", name: "chk_tag_description_versions_source_pair"
|
||||
t.check_constraint "(`source_language_code` is null) or (`source_language_code` <> `language_code`)", name: "chk_tag_description_versions_source_language_code_different"
|
||||
t.check_constraint "`event_type` in (_utf8mb4'create',_utf8mb4'update',_utf8mb4'discard',_utf8mb4'restore')", name: "tag_description_versions_event_type_valid"
|
||||
t.check_constraint "`version_no` > 0", name: "chk_tag_description_versions_version_no_positive"
|
||||
end
|
||||
|
||||
create_table "tag_descriptions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "tag_id", null: false
|
||||
t.string "language_code", limit: 16, null: false
|
||||
t.integer "version_no", null: false
|
||||
t.text "body", null: false
|
||||
t.string "source_language_code", limit: 16
|
||||
t.integer "source_version_no"
|
||||
t.bigint "created_by_user_id"
|
||||
t.bigint "updated_by_user_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["created_by_user_id"], name: "index_tag_descriptions_on_created_by_user_id"
|
||||
t.index ["language_code"], name: "index_tag_descriptions_on_language_code"
|
||||
t.index ["source_language_code", "source_version_no"], name: "idx_on_source_language_code_source_version_no_00117fe02a"
|
||||
t.index ["tag_id", "language_code"], name: "index_tag_descriptions_on_tag_id_and_language_code", unique: true
|
||||
t.index ["tag_id", "source_language_code", "source_version_no"], name: "fk_rails_d1be0a7696"
|
||||
t.index ["tag_id"], name: "index_tag_descriptions_on_tag_id"
|
||||
t.index ["updated_by_user_id"], name: "index_tag_descriptions_on_updated_by_user_id"
|
||||
t.check_constraint "((`source_language_code` is null) and (`source_version_no` is null)) or ((`source_language_code` is not null) and (`source_version_no` is not null))", name: "chk_tag_descriptions_source_pair"
|
||||
t.check_constraint "(`source_language_code` is null) or (`source_language_code` <> `language_code`)", name: "chk_tag_descriptions_source_language_code_different"
|
||||
t.check_constraint "`version_no` > 0", name: "chk_tag_descriptions_version_no_positive"
|
||||
end
|
||||
|
||||
create_table "tag_implications", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "tag_id", null: false
|
||||
t.bigint "parent_tag_id", null: false
|
||||
@@ -656,19 +699,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
t.index ["banned_at"], name: "index_users_on_banned_at"
|
||||
end
|
||||
|
||||
create_table "wiki_assets", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.bigint "wiki_page_id", null: false
|
||||
t.integer "no", null: false
|
||||
t.string "alt_text"
|
||||
t.binary "sha256", limit: 32, null: false
|
||||
t.bigint "created_by_user_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["created_by_user_id"], name: "index_wiki_assets_on_created_by_user_id"
|
||||
t.index ["wiki_page_id", "no"], name: "index_wiki_assets_on_wiki_page_id_and_no", unique: true
|
||||
t.index ["wiki_page_id", "sha256"], name: "index_wiki_assets_on_wiki_page_id_and_sha256", unique: true
|
||||
end
|
||||
|
||||
create_table "wiki_lines", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.string "sha256", limit: 64, null: false
|
||||
t.text "body", null: false
|
||||
@@ -685,7 +715,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "discarded_at"
|
||||
t.integer "next_asset_no", default: 1, null: false
|
||||
t.integer "version_no", null: false
|
||||
t.index ["created_user_id"], name: "index_wiki_pages_on_created_user_id"
|
||||
t.index ["discarded_at"], name: "index_wiki_pages_on_discarded_at"
|
||||
@@ -788,6 +817,12 @@ ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
add_foreign_key "post_versions", "users", column: "created_by_user_id"
|
||||
add_foreign_key "posts", "users", column: "uploaded_user_id"
|
||||
add_foreign_key "settings", "users"
|
||||
add_foreign_key "tag_description_versions", "tag_description_versions", column: ["tag_id", "source_language_code", "source_version_no"], primary_key: ["tag_id", "language_code", "version_no"]
|
||||
add_foreign_key "tag_descriptions", "languages", column: "language_code", primary_key: "code"
|
||||
add_foreign_key "tag_descriptions", "tag_description_versions", column: ["tag_id", "source_language_code", "source_version_no"], primary_key: ["tag_id", "language_code", "version_no"]
|
||||
add_foreign_key "tag_descriptions", "tags"
|
||||
add_foreign_key "tag_descriptions", "users", column: "created_by_user_id"
|
||||
add_foreign_key "tag_descriptions", "users", column: "updated_by_user_id"
|
||||
add_foreign_key "tag_implications", "tags"
|
||||
add_foreign_key "tag_implications", "tags", column: "parent_tag_id"
|
||||
add_foreign_key "tag_names", "languages", column: "language_code", primary_key: "code"
|
||||
@@ -822,8 +857,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_09_22_040000) do
|
||||
add_foreign_key "user_post_views", "posts"
|
||||
add_foreign_key "user_post_views", "users"
|
||||
add_foreign_key "user_theme_slots", "users"
|
||||
add_foreign_key "wiki_assets", "users", column: "created_by_user_id"
|
||||
add_foreign_key "wiki_assets", "wiki_pages"
|
||||
add_foreign_key "wiki_pages", "tag_names"
|
||||
add_foreign_key "wiki_pages", "users", column: "created_user_id"
|
||||
add_foreign_key "wiki_pages", "users", column: "updated_user_id"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
require 'rails_helper'
|
||||
require_relative '../../db/migrate/20261003010000_migrate_tag_descriptions_from_wiki_pages'
|
||||
|
||||
RSpec.describe 'Tag description migration identity' do
|
||||
it 'preserves every revision ID and repeated body in an independent sequence' do
|
||||
user = create_member_user!
|
||||
tag_name = create(:tag_name, name: 'description_migration')
|
||||
page = Wiki::Commit.create_content!(
|
||||
tag_name:, body: 'A', created_by_user: user)
|
||||
revisions = [page.current_revision]
|
||||
['A', 'B', 'B', 'A'].each do |body|
|
||||
revisions << Wiki::Commit.content!(
|
||||
page:, body:, created_user: user, base_revision_id: revisions.last.id)
|
||||
end
|
||||
tag = create(:tag, primary_tag_name: tag_name)
|
||||
legacy_state = [page.reload.attributes, revisions.map(&:attributes)]
|
||||
expect(page.wiki_versions.count).to eq(3)
|
||||
|
||||
migration = MigrateTagDescriptionsFromWikiPages.new
|
||||
migration.suppress_messages { migration.up }
|
||||
|
||||
versions = TagDescriptionVersion.where(tag_id: tag.id, language_code: 'ja')
|
||||
.order(:version_no)
|
||||
expect(versions.pluck(:id)).to eq(revisions.map(&:id))
|
||||
expect(versions.pluck(:version_no)).to eq([1, 2, 3, 4, 5])
|
||||
expect(versions.pluck(:body)).to eq(['A', 'A', 'B', 'B', 'A'])
|
||||
expect(versions.pluck(:created_by_user_id)).to eq([user.id] * 5)
|
||||
expect(versions.pluck(:created_at)).to eq(revisions.map(&:created_at))
|
||||
expect(TagDescription.find(page.id)).to have_attributes(
|
||||
tag_id: tag.id, language_code: 'ja', version_no: 5, body: 'A')
|
||||
expect([page.reload.attributes, revisions.map { |revision| revision.reload.attributes }])
|
||||
.to eq(legacy_state)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,276 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Tag description Wiki compatibility', type: :request do
|
||||
let!(:user) { create_member_user! }
|
||||
let!(:tag_name) { create(:tag_name, name: 'description_contract') }
|
||||
let!(:page) do
|
||||
Wiki::Commit.create_content!(
|
||||
tag_name:, body: 'old-revision', created_by_user: user)
|
||||
end
|
||||
let!(:revisions) do
|
||||
first = page.current_revision
|
||||
second = Wiki::Commit.content!(page:, body: 'old-second', created_user: user)
|
||||
third = Wiki::Commit.content!(page:, body: 'old-third', created_user: user)
|
||||
[first, second, third]
|
||||
end
|
||||
let!(:tag) { create(:tag, primary_tag_name: tag_name) }
|
||||
let!(:versions) do
|
||||
revisions.each_with_index.map do |revision, index|
|
||||
TagDescriptionVersion.create!(
|
||||
id: revision.id, tag_id: tag.id, language_code: 'ja',
|
||||
version_no: index + 1, event_type: index.zero? ? 'create' : 'update',
|
||||
body: index == 2 ? 'new-version' : 'A',
|
||||
created_by_user_id: user.id, created_at: revision.created_at)
|
||||
end
|
||||
end
|
||||
let!(:description) do
|
||||
TagDescription.create!(
|
||||
id: page.id, tag:, language_code: 'ja', version_no: 3, body: 'new',
|
||||
created_by_user_id: user.id, updated_by_user_id: user.id)
|
||||
end
|
||||
|
||||
before { page.update_columns(body: 'old') }
|
||||
|
||||
# Observe real SELECTs, including cached queries, only during the request.
|
||||
def without_legacy_reads
|
||||
queries = []
|
||||
subscriber = lambda do |*args|
|
||||
sql = args.last.fetch(:sql)
|
||||
if sql.match?(/\bSELECT\b/i) && sql.match?(/\bwiki_(pages|revisions|versions)\b/i)
|
||||
queries << sql
|
||||
end
|
||||
end
|
||||
ActiveSupport::Notifications.subscribed(subscriber, 'sql.active_record') { yield }
|
||||
expect(queries).to be_empty
|
||||
end
|
||||
|
||||
describe 'show' do
|
||||
it 'uses current content and revision identity without legacy reads' do
|
||||
without_legacy_reads { get "/wiki/#{ description.id }" }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'new', 'revision_id' => versions.last.id,
|
||||
'pred' => versions.second.id, 'succ' => nil)
|
||||
end
|
||||
|
||||
[0, 1, 2].each do |index|
|
||||
it "navigates displayed version #{ index + 1 }, preserving repeated bodies" do
|
||||
without_legacy_reads do
|
||||
get "/wiki/#{ description.id }", params: { version: versions[index].id }
|
||||
end
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include(
|
||||
'body' => index == 2 ? 'new' : 'A',
|
||||
'revision_id' => versions[index].id,
|
||||
'pred' => index.zero? ? nil : versions[index - 1].id,
|
||||
'succ' => index == 2 ? nil : versions[index + 1].id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not fall back to a revision absent from new history' do
|
||||
versions.first.destroy!
|
||||
without_legacy_reads do
|
||||
get "/wiki/#{ description.id }", params: { version: revisions.first.id }
|
||||
end
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'does not fall back when the new current revision is missing' do
|
||||
versions.last.destroy!
|
||||
without_legacy_reads { get "/wiki/#{ description.id }" }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'revision resource scope' do
|
||||
let!(:other_tag) { create(:tag, primary_name: 'other_description_contract') }
|
||||
let!(:other_version) do
|
||||
TagDescriptionVersion.create!(
|
||||
tag_id: other_tag.id, language_code: 'ja', version_no: 1,
|
||||
event_type: 'create', body: 'other tag history',
|
||||
created_by_user_id: user.id, created_at: Time.current)
|
||||
end
|
||||
let!(:other_description) do
|
||||
TagDescription.create!(
|
||||
tag: other_tag, language_code: 'ja', version_no: 1, body: 'other tag history',
|
||||
created_by_user_id: user.id, updated_by_user_id: user.id)
|
||||
end
|
||||
|
||||
it 'returns 404 when show specifies another tag description version' do
|
||||
get "/wiki/#{ description.id }", params: { version: other_version.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).not_to include(other_version.body)
|
||||
end
|
||||
|
||||
it 'returns 404 when diff from belongs to another tag description' do
|
||||
get "/wiki/#{ description.id }/diff",
|
||||
params: { from: other_version.id, to: versions.last.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).not_to include(other_version.body)
|
||||
end
|
||||
|
||||
it 'returns 404 when diff to belongs to another tag description' do
|
||||
get "/wiki/#{ description.id }/diff",
|
||||
params: { from: versions.first.id, to: other_version.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).not_to include(other_version.body)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'show_by_title' do
|
||||
it 'resolves TagName and reads current content from new tables' do
|
||||
without_legacy_reads { get "/wiki/title/#{ tag_name.name }" }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('id' => description.id, 'body' => 'new',
|
||||
'revision_id' => versions.last.id)
|
||||
end
|
||||
|
||||
it 'uses historical content and navigation from the new history' do
|
||||
get "/wiki/title/#{ tag_name.name }", params: { version: versions.second.id }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'A', 'revision_id' => versions.second.id,
|
||||
'pred' => versions.first.id, 'succ' => versions.last.id)
|
||||
end
|
||||
|
||||
it 'ignores changes to same-title legacy content and revision metadata' do
|
||||
get "/wiki/title/#{ tag_name.name }"
|
||||
original = json
|
||||
page.update_columns(body: 'changed legacy body')
|
||||
revisions.last.update_columns(message: 'legacy message', created_at: 1.day.ago)
|
||||
revisions.last.wiki_lines.update_all(body: 'changed legacy revision')
|
||||
get "/wiki/title/#{ tag_name.name }"
|
||||
expect(json).to eq(original)
|
||||
expect(json.fetch('body')).to eq('new')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'exists' do
|
||||
it 'recognises an ID without reading legacy tables' do
|
||||
without_legacy_reads { get "/wiki/#{ description.id }/exists" }
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
it 'recognises a title without reading legacy tables' do
|
||||
without_legacy_reads { get "/wiki/title/#{ tag_name.name }/exists" }
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
context 'without a legacy page' do
|
||||
before do
|
||||
WikiVersion.where(wiki_page_id: page.id).delete_all
|
||||
page.destroy!
|
||||
end
|
||||
|
||||
it 'recognises the description ID alone' do
|
||||
get "/wiki/#{ description.id }/exists"
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
it 'recognises the TagName alone' do
|
||||
get "/wiki/title/#{ tag_name.name }/exists"
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
it 'shows the description by title' do
|
||||
get "/wiki/title/#{ tag_name.name }"
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'new', 'revision_id' => versions.last.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'diff' do
|
||||
[false, true].each do |current|
|
||||
it "uses new bodies with #{ current ? 'implicit current' : 'explicit to' }" do
|
||||
params = { from: versions.first.id }
|
||||
params[:to] = versions.last.id unless current
|
||||
get "/wiki/#{ description.id }/diff", params: params
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include(
|
||||
'wiki_page_id' => description.id,
|
||||
'older_revision_id' => versions.first.id,
|
||||
'newer_revision_id' => versions.last.id,
|
||||
'diff' => [{ 'type' => 'removed', 'content' => 'A' },
|
||||
{ 'type' => 'added', 'content' => 'new-version' }])
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not query legacy tables for diff' do
|
||||
without_legacy_reads do
|
||||
get "/wiki/#{ description.id }/diff",
|
||||
params: { from: versions.first.id, to: versions.last.id }
|
||||
end
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
|
||||
it 'keeps distinct revision IDs for identical consecutive bodies' do
|
||||
get "/wiki/#{ description.id }/diff",
|
||||
params: { from: versions.first.id, to: versions.second.id }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include(
|
||||
'older_revision_id' => versions.first.id,
|
||||
'newer_revision_id' => versions.second.id,
|
||||
'diff' => [{ 'type' => 'context', 'content' => 'A' }])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'changes' do
|
||||
it 'lists every new version including consecutive identical bodies' do
|
||||
without_legacy_reads { get '/wiki/changes', params: { id: description.id } }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json.map { |row| row.fetch('revision_id') }).to eq(versions.reverse.map(&:id))
|
||||
expect(json.first.keys).to match_array(
|
||||
['revision_id', 'pred', 'succ', 'wiki_page', 'user', 'kind', 'message', 'timestamp'])
|
||||
expect(json.first).to include(
|
||||
'pred' => versions.second.id, 'succ' => nil, 'kind' => 'content',
|
||||
'timestamp' => versions.last.created_at.iso8601(3),
|
||||
'user' => { 'id' => user.id, 'name' => user.name })
|
||||
expect(json.first.fetch('wiki_page')).to include('id' => description.id,
|
||||
'title' => tag_name.name)
|
||||
end
|
||||
|
||||
it 'ignores legacy-only revisions and metadata changes' do
|
||||
get '/wiki/changes', params: { id: description.id }
|
||||
original = json
|
||||
revisions.first.update_columns(message: 'legacy changed', created_at: 1.day.ago)
|
||||
WikiRevision.create!(
|
||||
wiki_page: page, created_user: user, kind: :content,
|
||||
tree_sha256: Digest::SHA256.hexdigest('legacy only'), lines_count: 0)
|
||||
get '/wiki/changes', params: { id: description.id }
|
||||
expect(json).to eq(original)
|
||||
expect(json.map { |row| row.fetch('revision_id') }).to eq(versions.reverse.map(&:id))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'mixed changes' do
|
||||
it 'includes unmigrated history and ignores migrated legacy-only revisions' do
|
||||
legacy_page = Wiki::Commit.create_content!(
|
||||
tag_name: create(:tag_name, name: 'Help_mixed'),
|
||||
body: 'help', created_by_user: user)
|
||||
legacy_revision = legacy_page.current_revision
|
||||
extra = WikiRevision.create!(
|
||||
wiki_page: page, created_user: user, kind: :content,
|
||||
tree_sha256: Digest::SHA256.hexdigest('legacy only'), lines_count: 0)
|
||||
|
||||
get '/wiki/changes'
|
||||
expect(response).to have_http_status(:ok)
|
||||
migrated = json.select { |row| row.fetch('wiki_page').fetch('id') == description.id }
|
||||
expect(migrated.map { |row| row.fetch('revision_id') }).to eq(versions.reverse.map(&:id))
|
||||
expect(json.map { |row| row.fetch('revision_id') }).not_to include(extra.id)
|
||||
expect(json).to include(include(
|
||||
'revision_id' => legacy_revision.id,
|
||||
'wiki_page' => include('id' => legacy_page.id, 'title' => 'Help_mixed')))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'index' do
|
||||
[nil, 'description_contract'].each do |title|
|
||||
it "returns new content once in #{ title ? 'filtered' : 'unfiltered' } index" do
|
||||
get '/wiki', params: { title: }
|
||||
expect(response).to have_http_status(:ok)
|
||||
rows = json.select { |row| row.fetch('id') == description.id }
|
||||
expect(rows.size).to eq(1)
|
||||
expect(rows.first).to include('body' => 'new', 'title' => tag_name.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,162 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Tag description Wiki dual writes', type: :request do
|
||||
let!(:user) { create_member_user! }
|
||||
let!(:tag_name) { create(:tag_name, name: 'description_write') }
|
||||
let(:tag) { create(:tag, primary_tag_name: tag_name) }
|
||||
let(:headers) { { 'X-Transfer-Code' => user.inheritance_code } }
|
||||
|
||||
def stored_state
|
||||
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion,
|
||||
TagName, TagVersion].map do |model|
|
||||
model.order(:id).map(&:attributes)
|
||||
end
|
||||
end
|
||||
|
||||
# Fail after a real INSERT/UPDATE, exercising rollback rather than a mock write.
|
||||
def fail_save model
|
||||
failure = Class.new(StandardError)
|
||||
callback = ->(_record) { raise failure, 'injected persistence failure' }
|
||||
model.set_callback(:save, :after, callback)
|
||||
expect { yield }.to raise_error(failure)
|
||||
ensure
|
||||
model.skip_callback(:save, :after, callback)
|
||||
end
|
||||
|
||||
describe 'POST /wiki' do
|
||||
before { tag }
|
||||
|
||||
def create_description
|
||||
post '/wiki', params: { title: tag_name.name, body: 'initial', message: 'create' },
|
||||
headers: headers
|
||||
end
|
||||
|
||||
it 'creates both current records and histories with a shared revision ID' do
|
||||
expect { create_description }
|
||||
.to change(WikiPage, :count).by(1)
|
||||
.and change(WikiRevision, :count).by(1)
|
||||
.and change(WikiVersion, :count).by(1)
|
||||
.and change(TagDescription, :count).by(1)
|
||||
.and change(TagDescriptionVersion, :count).by(1)
|
||||
expect(response).to have_http_status(:created)
|
||||
|
||||
page = WikiPage.find(json.fetch('id'))
|
||||
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
||||
version = TagDescriptionVersion.find_by!(tag_id: tag.id, language_code: 'ja')
|
||||
expect(description).to have_attributes(id: page.id, body: 'initial', version_no: 1)
|
||||
expect(version).to have_attributes(
|
||||
id: page.current_revision.id, body: 'initial', version_no: 1,
|
||||
event_type: 'create', created_by_user_id: user.id)
|
||||
expect(page.body).to eq(description.body)
|
||||
expect(page.current_revision.body).to eq(version.body)
|
||||
expect(page.wiki_versions.last.body).to eq(version.body)
|
||||
end
|
||||
|
||||
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
||||
it "rolls back both systems when #{ model.name } persistence fails" do
|
||||
tag_name
|
||||
before_state = stored_state
|
||||
aggregate_failures do
|
||||
fail_save(model) { create_description }
|
||||
expect(stored_state).to eq(before_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /wiki/:id' do
|
||||
let!(:page) do
|
||||
Wiki::Commit.create_content!(
|
||||
tag_name:, body: 'before', created_by_user: user)
|
||||
end
|
||||
let!(:first_revision) { page.current_revision }
|
||||
let!(:current_revision) do
|
||||
# Repeated bodies create revisions, but no extra WikiVersion snapshot.
|
||||
Wiki::Commit.content!(page:, body: 'before', created_user: user,
|
||||
base_revision_id: first_revision.id)
|
||||
end
|
||||
let!(:description) do
|
||||
TagDescription.create!(
|
||||
id: page.id, tag:, language_code: 'ja', body: 'before', version_no: 2,
|
||||
created_by_user_id: user.id, updated_by_user_id: user.id)
|
||||
end
|
||||
|
||||
before do
|
||||
[first_revision, current_revision].each_with_index do |revision, index|
|
||||
TagDescriptionVersion.create!(
|
||||
id: revision.id, tag_id: tag.id, language_code: 'ja', version_no: index + 1,
|
||||
body: 'before', event_type: index.zero? ? 'create' : 'update',
|
||||
created_by_user_id: user.id, created_at: revision.created_at)
|
||||
end
|
||||
end
|
||||
|
||||
def update_description body: 'after', title: tag_name.name, base: current_revision.id
|
||||
put "/wiki/#{ page.id }",
|
||||
params: { title:, body:, message: 'edit', base_revision_id: base },
|
||||
headers: headers
|
||||
end
|
||||
|
||||
it 'increments the independent description sequence and shares revision identity' do
|
||||
expect(page.reload.version_no).to eq(1)
|
||||
expect(description.version_no).to eq(2)
|
||||
expect { update_description }
|
||||
.to change(WikiRevision, :count).by(1)
|
||||
.and change(WikiVersion, :count).by(1)
|
||||
.and change(TagDescriptionVersion, :count).by(1)
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
revision = page.reload.current_revision
|
||||
version = TagDescriptionVersion.find(revision.id)
|
||||
expect(description.reload).to have_attributes(body: 'after', version_no: 3)
|
||||
expect(version).to have_attributes(
|
||||
body: 'after', version_no: 3, event_type: 'update', created_by_user_id: user.id)
|
||||
expect(page).to have_attributes(body: 'after', version_no: 2)
|
||||
expect(revision.body).to eq(version.body)
|
||||
expect(page.wiki_versions.order(:version_no).last.body).to eq(version.body)
|
||||
end
|
||||
|
||||
it 'mirrors the Wiki revision for a title-only update despite identical bodies' do
|
||||
expect { update_description(body: 'before', title: 'description_renamed') }
|
||||
.to change(WikiRevision, :count).by(1)
|
||||
.and change(TagDescriptionVersion, :count).by(1)
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(tag_name.reload.name).to eq('description_renamed')
|
||||
expect(description.reload).to have_attributes(body: 'before', version_no: 3)
|
||||
|
||||
revision = page.reload.current_revision
|
||||
version = TagDescriptionVersion.find(revision.id)
|
||||
expect(revision.body).to eq('before')
|
||||
expect(version).to have_attributes(
|
||||
tag_id: tag.id, language_code: 'ja', body: 'before', version_no: 3,
|
||||
event_type: 'update', created_by_user_id: user.id)
|
||||
expect(TagDescriptionVersion.where(tag_id: tag.id, language_code: 'ja').order(:id).ids)
|
||||
.to eq(page.wiki_revisions.order(:id).ids)
|
||||
end
|
||||
|
||||
it 'does not add a description version for a TagName rename through tags' do
|
||||
expect do
|
||||
patch "/tags/#{ tag.id }", params: { name: 'description_tag_renamed' }, headers: headers
|
||||
end.not_to change(TagDescriptionVersion, :count)
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(tag_name.reload.name).to eq('description_tag_renamed')
|
||||
expect(description.reload).to have_attributes(body: 'before', version_no: 2)
|
||||
end
|
||||
|
||||
it 'keeps both systems and the title unchanged on a stale revision conflict' do
|
||||
before_state = stored_state
|
||||
update_description(title: 'conflicting_rename', base: first_revision.id)
|
||||
expect(response).to have_http_status(:conflict)
|
||||
expect(stored_state).to eq(before_state)
|
||||
end
|
||||
|
||||
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
||||
it "rolls back both systems when #{ model.name } persistence fails" do
|
||||
before_state = stored_state
|
||||
aggregate_failures do
|
||||
fail_save(model) { update_description(title: 'rollback_rename') }
|
||||
expect(stored_state).to eq(before_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,96 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Unmigrated Wiki compatibility', type: :request do
|
||||
let!(:user) { create_member_user! }
|
||||
let!(:tag_name) { create(:tag_name, name: 'Help_contract') }
|
||||
let!(:page) do
|
||||
Wiki::Commit.create_content!(tag_name:, body: 'before', created_by_user: user)
|
||||
end
|
||||
let!(:first_revision) { page.current_revision }
|
||||
let!(:last_revision) do
|
||||
Wiki::Commit.content!(page:, body: 'after', created_user: user,
|
||||
message: 'legacy edit', base_revision_id: first_revision.id)
|
||||
end
|
||||
|
||||
before { expect(TagDescription.exists?(id: page.id)).to be(false) }
|
||||
|
||||
it 'shows legacy current and historical content by ID' do
|
||||
get "/wiki/#{ page.id }"
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'after', 'revision_id' => last_revision.id,
|
||||
'pred' => first_revision.id, 'succ' => nil)
|
||||
get "/wiki/#{ page.id }", params: { version: first_revision.id }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'before', 'revision_id' => first_revision.id,
|
||||
'pred' => nil, 'succ' => last_revision.id)
|
||||
end
|
||||
|
||||
it 'shows legacy current and historical content by title' do
|
||||
get "/wiki/title/#{ tag_name.name }"
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'after', 'revision_id' => last_revision.id)
|
||||
get "/wiki/title/#{ tag_name.name }", params: { version: first_revision.id }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include('body' => 'before', 'revision_id' => first_revision.id)
|
||||
end
|
||||
|
||||
it 'recognises legacy IDs' do
|
||||
get "/wiki/#{ page.id }/exists"
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
it 'recognises legacy titles' do
|
||||
get "/wiki/title/#{ tag_name.name }/exists"
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
it 'diffs legacy revisions with explicit and current target' do
|
||||
[last_revision.id, nil].each do |to|
|
||||
get "/wiki/#{ page.id }/diff", params: { from: first_revision.id, to: }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json).to include(
|
||||
'wiki_page_id' => page.id, 'title' => tag_name.name,
|
||||
'older_revision_id' => first_revision.id, 'newer_revision_id' => last_revision.id,
|
||||
'diff' => [{ 'type' => 'removed', 'content' => 'before' },
|
||||
{ 'type' => 'added', 'content' => 'after' }])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'revision resource scope' do
|
||||
let!(:other_page) do
|
||||
Wiki::Commit.create_content!(
|
||||
tag_name: create(:tag_name, name: 'Help_other_contract'),
|
||||
body: 'other page history', created_by_user: user)
|
||||
end
|
||||
let(:other_revision) { other_page.current_revision }
|
||||
|
||||
it 'returns 404 when show specifies another page revision' do
|
||||
get "/wiki/#{ page.id }", params: { version: other_revision.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns 404 when diff from belongs to another page' do
|
||||
get "/wiki/#{ page.id }/diff",
|
||||
params: { from: other_revision.id, to: last_revision.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns 404 when diff to belongs to another page' do
|
||||
get "/wiki/#{ page.id }/diff",
|
||||
params: { from: first_revision.id, to: other_revision.id }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
it 'retains legacy history metadata and ordering' do
|
||||
get '/wiki/changes', params: { id: page.id }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json.map { |row| row.fetch('revision_id') }).to eq(
|
||||
[last_revision.id, first_revision.id])
|
||||
expect(json.first).to include(
|
||||
'pred' => first_revision.id, 'succ' => nil, 'kind' => 'content',
|
||||
'message' => 'legacy edit', 'timestamp' => last_revision.created_at.iso8601(3),
|
||||
'wiki_page' => { 'id' => page.id, 'title' => tag_name.name, 'deprecated_at' => nil },
|
||||
'user' => { 'id' => user.id, 'name' => user.name })
|
||||
end
|
||||
end
|
||||
新しいイシューから参照
ユーザーをブロックする