From fa5ef5af9adee96109c0ff81f49d4f5cb24df98f Mon Sep 17 00:00:00 2001 From: Miles Zhang Date: Mon, 18 Sep 2023 09:14:45 +0800 Subject: [PATCH] Issue 374 2 (#1450) * feat: generate udt verification model to save token Signed-off-by: Miles Zhang * feat: set mailer for udt verify Signed-off-by: Miles Zhang * feat: rename udt's contact_info to email Signed-off-by: Miles Zhang * feat: update udt info and send token business logic Signed-off-by: Miles Zhang --------- Signed-off-by: Miles Zhang --- .../api/v1/udt_verifications_controller.rb | 31 +++ app/controllers/api/v1/udts_controller.rb | 17 +- app/lib/api/v1/exceptions.rb | 37 +++ app/mailers/udt_verification_mailer.rb | 12 + app/models/udt.rb | 8 +- app/models/udt_verification.rb | 43 ++++ .../send_token.en.text.erb | 12 + .../send_token.zh_CN.text.erb | 10 + config/environments/production.rb | 14 +- config/initializers/locales.rb | 2 + config/routes.rb | 3 +- ...20230913091025_create_udt_verifications.rb | 14 ++ ...28_change_contract_info_to_email_in_udt.rb | 5 + db/structure.sql | 70 +++++- .../v1/udt_verifications_controller_test.rb | 47 ++++ .../api/v1/udts_controller_test.rb | 214 ++++++++++++------ test/factories/udt_verifications.rb | 9 + .../send_token_email.en.text.erb | 12 + .../send_token_email.zh_CN.text.erb | 10 + .../udt_verification_mailer_preview.rb | 5 + test/mailers/udt_verification_mailer_test.rb | 27 +++ test/models/udt_verification_test.rb | 7 + 22 files changed, 534 insertions(+), 75 deletions(-) create mode 100644 app/controllers/api/v1/udt_verifications_controller.rb create mode 100644 app/mailers/udt_verification_mailer.rb create mode 100644 app/models/udt_verification.rb create mode 100644 app/views/udt_verification_mailer/send_token.en.text.erb create mode 100644 app/views/udt_verification_mailer/send_token.zh_CN.text.erb create mode 100644 config/initializers/locales.rb create mode 100644 db/migrate/20230913091025_create_udt_verifications.rb create mode 100644 db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb create mode 100644 test/controllers/api/v1/udt_verifications_controller_test.rb create mode 100644 test/factories/udt_verifications.rb create mode 100644 test/fixtures/udt_verification_mailer/send_token_email.en.text.erb create mode 100644 test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb create mode 100644 test/mailers/previews/udt_verification_mailer_preview.rb create mode 100644 test/mailers/udt_verification_mailer_test.rb create mode 100644 test/models/udt_verification_test.rb diff --git a/app/controllers/api/v1/udt_verifications_controller.rb b/app/controllers/api/v1/udt_verifications_controller.rb new file mode 100644 index 000000000..2ee731e8e --- /dev/null +++ b/app/controllers/api/v1/udt_verifications_controller.rb @@ -0,0 +1,31 @@ +module Api + module V1 + class UdtVerificationsController < ApplicationController + before_action :check_udt_info, only: :update + before_action :set_locale, only: :update + + def update + udt_verification = UdtVerification.find_or_create_by(udt_id: @udt.id) + + udt_verification.refresh_token!(request.remote_ip) + UdtVerificationMailer.with(email: @udt.email, token: udt_verification.token, + locale: @locale).send_token.deliver_later + render json: :ok + rescue UdtVerification::TokenSentTooFrequentlyError + raise Api::V1::Exceptions::TokenSentTooFrequentlyError + end + + private + + def check_udt_info + @udt = Udt.find_by(type_hash: params[:id]) + raise Api::V1::Exceptions::UdtNotFoundError if @udt.nil? + raise Api::V1::Exceptions::UdtNoContactEmailError if @udt.email.blank? + end + + def set_locale + @locale = params[:locale] == "zh_CN" ? "zh_CN" : "en" + end + end + end +end diff --git a/app/controllers/api/v1/udts_controller.rb b/app/controllers/api/v1/udts_controller.rb index e9f598c90..3ad82eae0 100644 --- a/app/controllers/api/v1/udts_controller.rb +++ b/app/controllers/api/v1/udts_controller.rb @@ -34,14 +34,27 @@ def update icon_file: params[:icon_file], uan: params[:uan], display_name: params[:display_name], - contact_info: params[:contact_info] + email: params[:email] } - udt.update!(attrs) + if udt.email.blank? + raise Api::V1::Exceptions::UdtInfoInvalidError.new("Email can't be blank") if params[:email].blank? + + udt.update!(attrs) + else + raise Api::V1::Exceptions::UdtVerificationNotFoundError if udt.udt_verification.nil? + + udt.udt_verification.validate_token!(params[:token]) + udt.update!(attrs) + end render json: :ok rescue ActiveRecord::RecordNotFound raise Api::V1::Exceptions::UdtNotFoundError rescue ActiveRecord::RecordInvalid => e raise Api::V1::Exceptions::UdtInfoInvalidError.new(e) + rescue UdtVerification::TokenExpiredError + raise Api::V1::Exceptions::TokenExpiredError + rescue UdtVerification::TokenNotMatchError + raise Api::V1::Exceptions::TokenNotMatchError end def show diff --git a/app/lib/api/v1/exceptions.rb b/app/lib/api/v1/exceptions.rb index 1b9dce5f8..c011015fb 100644 --- a/app/lib/api/v1/exceptions.rb +++ b/app/lib/api/v1/exceptions.rb @@ -174,11 +174,13 @@ def initialize super code: 1027, status: 404, title: "URI parameters invalid", detail: "code hash should be start with 0x", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" end end + class ScriptHashTypeParamsInvalidError < Error def initialize super code: 1028, status: 404, title: "URI parameters invalid", detail: "hash type should be 'type'", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" end end + class ScriptNotFoundError < Error def initialize super code: 1029, status: 404, title: "Script not found", detail: "Script not found", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" @@ -191,6 +193,41 @@ def initialize(detail) end end + class UdtVerificationInvalidError < Error + def initialize(detail) + super code: 1031, status: 400, title: "UDT verification invalid", detail: detail, href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class UdtVerificationNotFoundError < Error + def initialize + super code: 1032, status: 404, title: "UDT Verification Not Found", detail: "No UDT verification records found by given type hash", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class UdtNoContactEmailError < Error + def initialize + super code: 1033, status: 400, title: "UDT has no contact email", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenExpiredError < Error + def initialize + super code: 1034, status: 400, title: "Token has expired", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenNotMatchError < Error + def initialize + super code: 1035, status: 400, title: "Token is not matched", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end + + class TokenSentTooFrequentlyError < Error + def initialize + super code: 1036, status: 400, title: "Token sent too frequently", detail: "", href: "https://nervosnetwork.github.io/ckb-explorer/public/api_doc.html" + end + end end end end diff --git a/app/mailers/udt_verification_mailer.rb b/app/mailers/udt_verification_mailer.rb new file mode 100644 index 000000000..886dc9419 --- /dev/null +++ b/app/mailers/udt_verification_mailer.rb @@ -0,0 +1,12 @@ +class UdtVerificationMailer < ApplicationMailer + default from: "noreply@magickbase.com" + + def send_token + email = params[:email] + @token = params[:token] + locale = params[:locale] || "en" + I18n.with_locale(locale) do + mail(to: email, subject: "Token Info Verification") + end + end +end diff --git a/app/models/udt.rb b/app/models/udt.rb index 78aea622d..0b73b73b5 100644 --- a/app/models/udt.rb +++ b/app/models/udt.rb @@ -1,7 +1,10 @@ class Udt < ApplicationRecord + MAX_PAGINATES_PER = 100 + belongs_to :nrc_factory_cell, optional: true - MAX_PAGINATES_PER = 100 + has_one :udt_verification + enum udt_type: { sudt: 0, m_nft_token: 1, nrc_721_token: 2, spore_cell: 3 } validates_presence_of :total_amount @@ -9,6 +12,7 @@ class Udt < ApplicationRecord validates_length_of :full_name, minimum: 1, maximum: 100, allow_nil: true validates :decimal, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 39 }, allow_nil: true validates :total_amount, numericality: { greater_than_or_equal_to: 0 } + validates :email, format: { with: /\A(.+)@(.+)\z/, message: "Not a valid email" }, allow_nil: true attribute :code_hash, :ckb_hash @@ -60,7 +64,7 @@ def type_script # display_name :string # uan :string # h24_ckb_transactions_count :bigint default(0) -# contact_info :string +# email :string # # Indexes # diff --git a/app/models/udt_verification.rb b/app/models/udt_verification.rb new file mode 100644 index 000000000..f92c4276b --- /dev/null +++ b/app/models/udt_verification.rb @@ -0,0 +1,43 @@ +class UdtVerification < ApplicationRecord + SENT_FREQUENCY_MINUTES = 1 + KEEP_ALIVE_MINUTES = 10 + + class TokenExpiredError < StandardError; end + class TokenNotMatchError < StandardError; end + class TokenSentTooFrequentlyError < StandardError; end + + belongs_to :udt + + def refresh_token!(ip) + raise TokenSentTooFrequentlyError if sent_at.present? && self.sent_at + SENT_FREQUENCY_MINUTES.minutes > Time.now + + self.token = rand(999999).to_s.rjust(6, "0") + self.sent_at = Time.now + self.last_ip = ip + self.save! + end + + def validate_token!(token_params) + raise TokenExpiredError if self.sent_at + KEEP_ALIVE_MINUTES.minutes < Time.now + raise TokenNotMatchError if token != token_params.to_i + end +end + +# == Schema Information +# +# Table name: udt_verifications +# +# id :bigint not null, primary key +# token :integer +# sent_at :datetime +# last_ip :inet +# udt_id :bigint +# udt_type_hash :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_udt_verifications_on_udt_id (udt_id) +# index_udt_verifications_on_udt_type_hash (udt_type_hash) UNIQUE +# diff --git a/app/views/udt_verification_mailer/send_token.en.text.erb b/app/views/udt_verification_mailer/send_token.en.text.erb new file mode 100644 index 000000000..f167d24fb --- /dev/null +++ b/app/views/udt_verification_mailer/send_token.en.text.erb @@ -0,0 +1,12 @@ +Dear Token Info Author, + +To ensure the effectiveness of your Token Info modification operation, we have generated a verification code for you to complete the Token Info update. + +Your verification code is: <%= @token %> + +Please note that this verification code is only valid for the next 10 minutes. Please use it to complete your Token Info modification within this time frame and refrain from sharing your code with others. If you have not initiated any action or if this operation is not associated with you, please disregard this email. + +If you have any questions or require assistance, please feel free to contact our customer support team.Thank you for your trust and support! + +Best regards, +The MagicKBase Team diff --git a/app/views/udt_verification_mailer/send_token.zh_CN.text.erb b/app/views/udt_verification_mailer/send_token.zh_CN.text.erb new file mode 100644 index 000000000..c307071f8 --- /dev/null +++ b/app/views/udt_verification_mailer/send_token.zh_CN.text.erb @@ -0,0 +1,10 @@ +尊敬的 Token Info 作者, + +为了确保本次 Token Info 修改操作的有效性,我们为您生成了一个验证码,用于完成 Token Info 的修改操作。 + +您的验证码是:<%= @token %> + +请注意,该验证码仅在接下来的10分钟内有效。请在此时间内完成 Token Info 的修改操作,并且请不要分享您的验证码给他人。如果您没有进行任何操作或者不是您的操作,请忽略此邮件。 + +如果您有任何疑问或需要帮助,请随时联系我们的客户支持团队。谢谢您的信任和支持! +MagicKBase 团队 diff --git a/config/environments/production.rb b/config/environments/production.rb index 4d2453274..d7172ea41 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -66,7 +66,19 @@ # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. - # config.action_mailer.raise_delivery_errors = false + config.action_mailer.raise_delivery_errors = true + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: ENV["SMTP_ADDRESS"], + port: ENV["SMTP_PORT"], + domain: "noreply@magickbase.com", + user_name: ENV["SMTP_USER"], + password: ENV["SMTP_PASSWORD"], + authentication: "plain", + enable_starttls_auto: true, + open_timeout: 5, + read_timeout: 5 + } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). diff --git a/config/initializers/locales.rb b/config/initializers/locales.rb new file mode 100644 index 000000000..c7d592062 --- /dev/null +++ b/config/initializers/locales.rb @@ -0,0 +1,2 @@ +I18n.available_locales = [:en, :zh_CN] +I18n.default_locale = :en diff --git a/config/routes.rb b/config/routes.rb index 3e3e17fd6..ca2ae99cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,7 +51,7 @@ resources :block_statistics, only: :show ## TODO: unused route resources :epoch_statistics, only: :show resources :market_data, only: :show - resources :udts, only: %i(index show) do + resources :udts, only: %i(index show update) do collection do get :download_csv end @@ -60,6 +60,7 @@ resources :address_udt_transactions, only: :show resources :distribution_data, only: :show resources :monetary_data, only: :show + resources :udt_verifications, only: :update end end draw "v2" diff --git a/db/migrate/20230913091025_create_udt_verifications.rb b/db/migrate/20230913091025_create_udt_verifications.rb new file mode 100644 index 000000000..5441b35b9 --- /dev/null +++ b/db/migrate/20230913091025_create_udt_verifications.rb @@ -0,0 +1,14 @@ +class CreateUdtVerifications < ActiveRecord::Migration[7.0] + def change + create_table :udt_verifications do |t| + t.integer :token + t.datetime :sent_at + t.inet :last_ip + t.belongs_to :udt + t.integer :udt_type_hash + + t.timestamps + t.index :udt_type_hash, unique: true + end + end +end diff --git a/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb b/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb new file mode 100644 index 000000000..163bcb6f1 --- /dev/null +++ b/db/migrate/20230914120928_change_contract_info_to_email_in_udt.rb @@ -0,0 +1,5 @@ +class ChangeContractInfoToEmailInUdt < ActiveRecord::Migration[7.0] + def change + rename_column :udts, :contact_info, :email + end +end diff --git a/db/structure.sql b/db/structure.sql index 2d66ef4b6..341b75993 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2195,6 +2195,41 @@ CREATE TABLE public.udt_transactions ( ); +-- +-- Name: udt_verifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.udt_verifications ( + id bigint NOT NULL, + token integer, + sent_at timestamp(6) without time zone, + last_ip inet, + udt_id bigint, + udt_type_hash integer, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: udt_verifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.udt_verifications_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: udt_verifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.udt_verifications_id_seq OWNED BY public.udt_verifications.id; + + -- -- Name: udts; Type: TABLE; Schema: public; Owner: - -- @@ -2224,7 +2259,7 @@ CREATE TABLE public.udts ( display_name character varying, uan character varying, h24_ckb_transactions_count bigint DEFAULT 0, - contact_info character varying + email character varying ); @@ -2638,6 +2673,13 @@ ALTER TABLE ONLY public.type_scripts ALTER COLUMN id SET DEFAULT nextval('public ALTER TABLE ONLY public.udt_accounts ALTER COLUMN id SET DEFAULT nextval('public.udt_accounts_id_seq'::regclass); +-- +-- Name: udt_verifications id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_verifications ALTER COLUMN id SET DEFAULT nextval('public.udt_verifications_id_seq'::regclass); + + -- -- Name: udts id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3091,6 +3133,14 @@ ALTER TABLE ONLY public.udt_accounts ADD CONSTRAINT udt_accounts_pkey PRIMARY KEY (id); +-- +-- Name: udt_verifications udt_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_verifications + ADD CONSTRAINT udt_verifications_pkey PRIMARY KEY (id); + + -- -- Name: udts udts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4127,6 +4177,20 @@ CREATE INDEX index_udt_transactions_on_ckb_transaction_id ON public.udt_transact CREATE INDEX index_udt_transactions_on_udt_id ON public.udt_transactions USING btree (udt_id); +-- +-- Name: index_udt_verifications_on_udt_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_udt_verifications_on_udt_id ON public.udt_verifications USING btree (udt_id); + + +-- +-- Name: index_udt_verifications_on_udt_type_hash; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_udt_verifications_on_udt_type_hash ON public.udt_verifications USING btree (udt_type_hash); + + -- -- Name: index_udts_on_type_hash; Type: INDEX; Schema: public; Owner: - -- @@ -4684,6 +4748,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230711040233'), ('20230802015907'), ('20230808020637'), -('20230829061910'); +('20230829061910'), +('20230913091025'), +('20230914120928'); diff --git a/test/controllers/api/v1/udt_verifications_controller_test.rb b/test/controllers/api/v1/udt_verifications_controller_test.rb new file mode 100644 index 000000000..18de08a91 --- /dev/null +++ b/test/controllers/api/v1/udt_verifications_controller_test.rb @@ -0,0 +1,47 @@ +require "test_helper" + +module Api + module V1 + class UdtVerificationsControllerTest < ActionDispatch::IntegrationTest + test "raise error when udt not exist" do + valid_put api_v1_udt_verification_url("0x#{SecureRandom.hex(32)}") + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Not Found", "detail" => "No UDT records found by given type hash", "code" => 1026, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise error when udt no contact mail" do + udt = create(:udt, published: true) + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT has no contact email", "detail" => "", "code" => 1033, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise error when sent too frequently" do + udt = create(:udt, published: true, email: "example@sudt.com") + create(:udt_verification, udt: udt, udt_type_hash: udt.type_hash) + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 400, response.status + assert_equal [{ "title" => "Token sent too frequently", "detail" => "", "code" => 1036, "status" => 400 }], + JSON.parse(response.body) + end + + test "should sent successfully" do + udt = create(:udt, published: true, email: "example@sudt.com") + valid_put api_v1_udt_verification_url(udt.type_hash) + + assert_equal 200, response.status + assert_equal "ok", JSON.parse(response.body) + uv = UdtVerification.first + assert_not_nil uv.token + assert_not_nil uv.sent_at + assert_not_nil uv.last_ip + assert_equal ActiveJob::Base.queue_adapter.enqueued_jobs[0][:args][0], "UdtVerificationMailer" + end + end + end +end diff --git a/test/controllers/api/v1/udts_controller_test.rb b/test/controllers/api/v1/udts_controller_test.rb index 052bfc56a..f833acfb4 100644 --- a/test/controllers/api/v1/udts_controller_test.rb +++ b/test/controllers/api/v1/udts_controller_test.rb @@ -415,73 +415,153 @@ class UdtsControllerTest < ActionDispatch::IntegrationTest assert_response :success end - # test "should update udt info suceessfully" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url(udt.type_hash), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # total_amount: "100000000000", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)", - # contact_info: "contact@usdt.com" - # } - - # assert_response :success - # udt.reload - # assert_equal udt.symbol, "GWK" - # assert_equal udt.full_name, "GodwokenToken on testnet_v1" - # assert_equal udt.decimal, 8 - # assert_equal udt.total_amount, 100000000000 - # assert_equal udt.description, "The sUDT_ERC20_Proxy of Godwoken Test Token." - # assert_equal udt.operator_website, "https://udt.coin" - # assert_equal udt.icon_file, "https://img.udt.img" - # assert_equal udt.uan, "GWK.gw|gb.ckb" - # assert_equal udt.display_name, "GodwokenToken (via Godwoken Bridge from CKB)" - # assert_equal udt.contact_info, "contact@usdt.com" - - # end - - # test "raise parameters error when update udt" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url(udt.type_hash), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)" - # } - - # assert_equal 400, response.status - # assert_equal response.body, "[{\"title\":\"UDT info parameters invalid\",\"detail\":\"Validation failed: Total amount can't be blank, Total amount is not a number\",\"code\":1030,\"status\":400}]" - # end - - # test "raise not found error when update udt" do - # udt = create(:udt, published: true) - - # valid_put api_v1_udt_url("#{udt.type_hash}0"), params: { - # symbol: "GWK", - # full_name: "GodwokenToken on testnet_v1", - # decimal: "8", - # total_amount: "100000000", - # description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", - # operator_website: "https://udt.coin", - # icon_file: "https://img.udt.img", - # uan: "GWK.gw|gb.ckb", - # display_name: "GodwokenToken (via Godwoken Bridge from CKB)" - # } - - # assert_equal 404, response.status - # assert_equal response.body, "[{\"title\":\"UDT Not Found\",\"detail\":\"No UDT records found by given type hash\",\"code\":1026,\"status\":404}]" - # end + test "should submit udt info suceessfully" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)", + email: "contact@usdt.com" + } + + assert_response :success + udt.reload + assert_equal udt.symbol, "GWK" + assert_equal udt.full_name, "GodwokenToken on testnet_v1" + assert_equal udt.decimal, 8 + assert_equal udt.total_amount, 100000000000 + assert_equal udt.description, "The sUDT_ERC20_Proxy of Godwoken Test Token." + assert_equal udt.operator_website, "https://udt.coin" + assert_equal udt.icon_file, "https://img.udt.img" + assert_equal udt.uan, "GWK.gw|gb.ckb" + assert_equal udt.display_name, "GodwokenToken (via Godwoken Bridge from CKB)" + assert_equal udt.email, "contact@usdt.com" + end + + test "raise email blank error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT info parameters invalid", "detail" => "Email can't be blank", "code" => 1030, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise email format error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url(udt.type_hash), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)", + email: "abcdefg" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "UDT info parameters invalid", "detail" => "Validation failed: Email Not a valid email", "code" => 1030, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise not found error when submit udt" do + udt = create(:udt, published: true) + + valid_put api_v1_udt_url("#{udt.type_hash}0"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + decimal: "8", + total_amount: "100000000", + description: "The sUDT_ERC20_Proxy of Godwoken Test Token.", + operator_website: "https://udt.coin", + icon_file: "https://img.udt.img", + uan: "GWK.gw|gb.ckb", + display_name: "GodwokenToken (via Godwoken Bridge from CKB)" + } + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Not Found", "detail" => "No UDT records found by given type hash", "code" => 1026, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise no udt_verification error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + token: "123456" + } + + assert_equal 404, response.status + assert_equal [{ "title" => "UDT Verification Not Found", "detail" => "No UDT verification records found by given type hash", "code" => 1032, "status" => 404 }], + JSON.parse(response.body) + end + + test "raise udt_verification expired error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, sent_at: Time.now - 11.minutes, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123456" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "Token has expired", "detail" => "", "code" => 1034, "status" => 400 }], + JSON.parse(response.body) + end + + test "raise udt_verification token not match error when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123" + } + + assert_equal 400, response.status + assert_equal [{ "title" => "Token is not matched", "detail" => "", "code" => 1035, "status" => 400 }], + JSON.parse(response.body) + end + + test "should update successfully when update udt" do + udt = create(:udt, email: "abc@sudt.com", published: true) + create(:udt_verification, udt: udt) + valid_put api_v1_udt_url("#{udt.type_hash}"), params: { + symbol: "GWK", + full_name: "GodwokenToken on testnet_v1", + total_amount: "100000000", + token: "123456" + } + + assert_equal 200, response.status + assert_equal "ok", JSON.parse(response.body) + assert_equal "GWK", udt.reload.symbol + end end end end diff --git a/test/factories/udt_verifications.rb b/test/factories/udt_verifications.rb new file mode 100644 index 000000000..faeba6e38 --- /dev/null +++ b/test/factories/udt_verifications.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :udt_verification do + token { 123456 } + sent_at { Time.now } + last_ip { "127.0.0.1" } + udt_type_hash { "0x#{SecureRandom.hex(32)}" } + udt + end +end diff --git a/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb b/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb new file mode 100644 index 000000000..166d98fcf --- /dev/null +++ b/test/fixtures/udt_verification_mailer/send_token_email.en.text.erb @@ -0,0 +1,12 @@ +Dear Token Info Author, + +To ensure the effectiveness of your Token Info modification operation, we have generated a verification code for you to complete the Token Info update. + +Your verification code is: 123456 + +Please note that this verification code is only valid for the next 10 minutes. Please use it to complete your Token Info modification within this time frame and refrain from sharing your code with others. If you have not initiated any action or if this operation is not associated with you, please disregard this email. + +If you have any questions or require assistance, please feel free to contact our customer support team.Thank you for your trust and support! + +Best regards, +The MagicKBase Team diff --git a/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb b/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb new file mode 100644 index 000000000..eba324bd8 --- /dev/null +++ b/test/fixtures/udt_verification_mailer/send_token_email.zh_CN.text.erb @@ -0,0 +1,10 @@ +尊敬的 Token Info 作者, + +为了确保本次 Token Info 修改操作的有效性,我们为您生成了一个验证码,用于完成 Token Info 的修改操作。 + +您的验证码是:123456 + +请注意,该验证码仅在接下来的10分钟内有效。请在此时间内完成 Token Info 的修改操作,并且请不要分享您的验证码给他人。如果您没有进行任何操作或者不是您的操作,请忽略此邮件。 + +如果您有任何疑问或需要帮助,请随时联系我们的客户支持团队。谢谢您的信任和支持! +MagicKBase 团队 diff --git a/test/mailers/previews/udt_verification_mailer_preview.rb b/test/mailers/previews/udt_verification_mailer_preview.rb new file mode 100644 index 000000000..9f5a6cee4 --- /dev/null +++ b/test/mailers/previews/udt_verification_mailer_preview.rb @@ -0,0 +1,5 @@ +class UdtVerificationMailerPreview < ActionMailer::Preview + def send_token + UdtVerificationMailer.with(email: "receiver@example.com", token: "123456", locale: params[:locale]).send_token + end +end diff --git a/test/mailers/udt_verification_mailer_test.rb b/test/mailers/udt_verification_mailer_test.rb new file mode 100644 index 000000000..0193055cc --- /dev/null +++ b/test/mailers/udt_verification_mailer_test.rb @@ -0,0 +1,27 @@ +require "test_helper" + +class UdtVerificationMailerTest < ActionMailer::TestCase + test "send token" do + email = UdtVerificationMailer.with(email: "receiver@example.com", token: "123456").send_token + + assert_emails 1 do + email.deliver_now + end + + # Test the body of the sent email contains what we expect it to + assert_equal ["noreply@magickbase.com"], email.from + assert_equal ["receiver@example.com"], email.to + assert_equal "Token Info Verification", email.subject + assert_equal "#{read_fixture('send_token_email.en.text.erb').join}\n", email.body.to_s + end + + test "when zh_CN locale" do + email = UdtVerificationMailer.with(email: "receiver@example.com", token: "123456", locale: "zh_CN").send_token + + assert_emails 1 do + email.deliver_now + end + + assert_equal "#{read_fixture('send_token_email.zh_CN.text.erb').join}\n", email.body.to_s.tr("\r", "") + end +end diff --git a/test/models/udt_verification_test.rb b/test/models/udt_verification_test.rb new file mode 100644 index 000000000..0527a4ee2 --- /dev/null +++ b/test/models/udt_verification_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UdtVerificationTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end