From c112576b11d9e1075d33d9a236633b7ad13f57c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BF=E3=81=A6=E3=82=8B=E3=81=9E?= Date: Sat, 25 Apr 2026 20:46:49 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=8B=E3=82=B3=E3=83=8B=E3=82=B3=20DB=20?= =?UTF-8?q?=E9=80=86=E9=80=A3=E6=90=BA=20(#200)=20(#331)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #200 Co-authored-by: miteruzo Reviewed-on: https://git.miteruzo.com/miteruzo/btrc-hub/pulls/331 --- backend/lib/tasks/export_nico.rake | 23 ++++++ backend/spec/tasks/nico_export_spec.rb | 100 +++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 backend/lib/tasks/export_nico.rake create mode 100644 backend/spec/tasks/nico_export_spec.rb diff --git a/backend/lib/tasks/export_nico.rake b/backend/lib/tasks/export_nico.rake new file mode 100644 index 0000000..c0c6c9f --- /dev/null +++ b/backend/lib/tasks/export_nico.rake @@ -0,0 +1,23 @@ +namespace :nico do + desc 'ニコニコ DB 逆連携' + task export: :environment do + require 'open3' + + mysql_user = ENV.fetch('MYSQL_USER') + mysql_pass = ENV.fetch('MYSQL_PASS') + nizika_nico_path = ENV.fetch('NIZIKA_NICO_PATH') + + videos = Post.where('url LIKE ?', '%nicovideo.jp/watch/%').pluck(:url).filter_map { + _1[%r{nicovideo\.jp/watch/([^/?#]+)}, 1] + }.uniq + + next if videos.empty? + + _, stderr, status = Open3.capture3( + { 'MYSQL_USER' => mysql_user, 'MYSQL_PASS' => mysql_pass }, + 'python3', '-m', 'tracked_videos.put_bulk_upsert', *videos, + chdir: nizika_nico_path) + + raise stderr unless status.success? + end +end diff --git a/backend/spec/tasks/nico_export_spec.rb b/backend/spec/tasks/nico_export_spec.rb new file mode 100644 index 0000000..e6ed87c --- /dev/null +++ b/backend/spec/tasks/nico_export_spec.rb @@ -0,0 +1,100 @@ +require 'rails_helper' +require 'rake' +require 'open3' + +RSpec.describe 'nico:export' do + let(:task) { Rake::Task['nico:export'] } + let(:success_status) { instance_double(Process::Status, success?: true) } + let(:failure_status) { instance_double(Process::Status, success?: false) } + + def create_post(url) + Post.create!(url:) + end + + before(:all) do + Rails.application.load_tasks unless Rake::Task.task_defined?('nico:export') + end + + before do + task.reenable + + allow(ENV).to receive(:fetch).with('MYSQL_USER').and_return('mysql-user') + allow(ENV).to receive(:fetch).with('MYSQL_PASS').and_return('mysql-pass') + allow(ENV).to receive(:fetch).with('NIZIKA_NICO_PATH').and_return('/srv/nizika-nico') + end + + describe 'export' do + it 'exports nicovideo ids to shared nico DB' do + create_post('https://www.nicovideo.jp/watch/sm12345?ref=foo') + create_post('https://www.nicovideo.jp/watch/so67890#comments') + create_post('https://www.nicovideo.jp/watch/nm24680') + create_post('https://example.com/watch/sm99999') + + expect(Open3).to receive(:capture3) do |env, *args, **kwargs| + expect(env).to eq( + { + 'MYSQL_USER' => 'mysql-user', + 'MYSQL_PASS' => 'mysql-pass', + }, + ) + + expect(args.take(3)).to eq( + [ + 'python3', + '-m', + 'tracked_videos.put_bulk_upsert', + ], + ) + + expect(args.drop(3)).to contain_exactly( + 'sm12345', + 'so67890', + 'nm24680', + ) + + expect(kwargs).to eq(chdir: '/srv/nizika-nico') + + ['', '', success_status] + end + + task.invoke + end + + it 'deduplicates video ids' do + create_post('https://www.nicovideo.jp/watch/sm12345') + create_post('https://www.nicovideo.jp/watch/sm12345?from=1') + + expect(Open3).to receive(:capture3) do |_env, *args, **_kwargs| + expect(args.drop(3)).to eq(['sm12345']) + + ['', '', success_status] + end + + task.invoke + end + + it 'does not call python when there are no nicovideo posts' do + create_post('https://example.com/watch/sm12345') + + expect(Open3).not_to receive(:capture3) + + task.invoke + end + + it 'raises stderr when python command fails' do + create_post('https://www.nicovideo.jp/watch/sm12345') + + allow(Open3).to receive(:capture3).and_return( + [ + '', + 'bulk upsert failed', + failure_status, + ], + ) + + expect { + task.invoke + }.to raise_error(RuntimeError, 'bulk upsert failed') + end + end +end