j-law-ruby 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/README.md +109 -0
  4. data/Rakefile +87 -0
  5. data/ext/j_law_ruby/extconf.rb +34 -0
  6. data/lib/j_law_ruby/build_support.rb +129 -0
  7. data/lib/j_law_ruby/c_ffi.rb +662 -0
  8. data/lib/j_law_ruby.rb +532 -0
  9. data/rake_support/vendor_rust.rb +51 -0
  10. data/test/test_c_ffi_adapter.rb +46 -0
  11. data/test/test_consumption_tax.rb +66 -0
  12. data/test/test_gemspec.rb +82 -0
  13. data/test/test_income_tax.rb +77 -0
  14. data/test/test_income_tax_deductions.rb +82 -0
  15. data/test/test_real_estate.rb +98 -0
  16. data/test/test_stamp_tax.rb +68 -0
  17. data/test/test_withholding_tax.rb +65 -0
  18. data/vendor/rust/Cargo.lock +235 -0
  19. data/vendor/rust/Cargo.toml +12 -0
  20. data/vendor/rust/crates/j-law-c-ffi/Cargo.toml +20 -0
  21. data/vendor/rust/crates/j-law-c-ffi/j_law_c_ffi.h +493 -0
  22. data/vendor/rust/crates/j-law-c-ffi/src/lib.rs +1553 -0
  23. data/vendor/rust/crates/j-law-core/Cargo.toml +18 -0
  24. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/calculator.rs +216 -0
  25. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/context.rs +29 -0
  26. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/mod.rs +9 -0
  27. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/params.rs +24 -0
  28. data/vendor/rust/crates/j-law-core/src/domains/consumption_tax/policy.rs +34 -0
  29. data/vendor/rust/crates/j-law-core/src/domains/income_tax/assessment.rs +76 -0
  30. data/vendor/rust/crates/j-law-core/src/domains/income_tax/calculator.rs +222 -0
  31. data/vendor/rust/crates/j-law-core/src/domains/income_tax/context.rs +79 -0
  32. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/calculator.rs +167 -0
  33. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/context.rs +172 -0
  34. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/expense.rs +465 -0
  35. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/mod.rs +20 -0
  36. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/params.rs +205 -0
  37. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/personal.rs +324 -0
  38. data/vendor/rust/crates/j-law-core/src/domains/income_tax/deduction/types.rs +61 -0
  39. data/vendor/rust/crates/j-law-core/src/domains/income_tax/mod.rs +24 -0
  40. data/vendor/rust/crates/j-law-core/src/domains/income_tax/params.rs +109 -0
  41. data/vendor/rust/crates/j-law-core/src/domains/income_tax/policy.rs +103 -0
  42. data/vendor/rust/crates/j-law-core/src/domains/mod.rs +5 -0
  43. data/vendor/rust/crates/j-law-core/src/domains/real_estate/calculator.rs +197 -0
  44. data/vendor/rust/crates/j-law-core/src/domains/real_estate/context.rs +48 -0
  45. data/vendor/rust/crates/j-law-core/src/domains/real_estate/mod.rs +9 -0
  46. data/vendor/rust/crates/j-law-core/src/domains/real_estate/params.rs +43 -0
  47. data/vendor/rust/crates/j-law-core/src/domains/real_estate/policy.rs +40 -0
  48. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/calculator.rs +321 -0
  49. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/context.rs +408 -0
  50. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/mod.rs +12 -0
  51. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/params.rs +190 -0
  52. data/vendor/rust/crates/j-law-core/src/domains/stamp_tax/policy.rs +105 -0
  53. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/calculator.rs +247 -0
  54. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/context.rs +167 -0
  55. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/mod.rs +9 -0
  56. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/params.rs +80 -0
  57. data/vendor/rust/crates/j-law-core/src/domains/withholding_tax/policy.rs +49 -0
  58. data/vendor/rust/crates/j-law-core/src/error.rs +171 -0
  59. data/vendor/rust/crates/j-law-core/src/lib.rs +9 -0
  60. data/vendor/rust/crates/j-law-core/src/types/amount.rs +232 -0
  61. data/vendor/rust/crates/j-law-core/src/types/citation.rs +82 -0
  62. data/vendor/rust/crates/j-law-core/src/types/date.rs +280 -0
  63. data/vendor/rust/crates/j-law-core/src/types/mod.rs +11 -0
  64. data/vendor/rust/crates/j-law-core/src/types/rate.rs +219 -0
  65. data/vendor/rust/crates/j-law-core/src/types/rounding.rs +81 -0
  66. data/vendor/rust/crates/j-law-registry/Cargo.toml +15 -0
  67. data/vendor/rust/crates/j-law-registry/data/consumption_tax/consumption_tax.json +70 -0
  68. data/vendor/rust/crates/j-law-registry/data/income_tax/deductions.json +327 -0
  69. data/vendor/rust/crates/j-law-registry/data/income_tax/income_tax.json +352 -0
  70. data/vendor/rust/crates/j-law-registry/data/real_estate/brokerage_fee.json +125 -0
  71. data/vendor/rust/crates/j-law-registry/data/stamp_tax/stamp_tax.json +674 -0
  72. data/vendor/rust/crates/j-law-registry/data/withholding_tax/withholding_tax.json +70 -0
  73. data/vendor/rust/crates/j-law-registry/src/consumption_tax_loader.rs +325 -0
  74. data/vendor/rust/crates/j-law-registry/src/consumption_tax_schema.rs +49 -0
  75. data/vendor/rust/crates/j-law-registry/src/income_tax_deduction_loader.rs +636 -0
  76. data/vendor/rust/crates/j-law-registry/src/income_tax_deduction_schema.rs +111 -0
  77. data/vendor/rust/crates/j-law-registry/src/income_tax_loader.rs +445 -0
  78. data/vendor/rust/crates/j-law-registry/src/income_tax_schema.rs +44 -0
  79. data/vendor/rust/crates/j-law-registry/src/lib.rs +20 -0
  80. data/vendor/rust/crates/j-law-registry/src/loader.rs +221 -0
  81. data/vendor/rust/crates/j-law-registry/src/schema.rs +73 -0
  82. data/vendor/rust/crates/j-law-registry/src/stamp_tax_loader.rs +374 -0
  83. data/vendor/rust/crates/j-law-registry/src/stamp_tax_schema.rs +72 -0
  84. data/vendor/rust/crates/j-law-registry/src/validator.rs +204 -0
  85. data/vendor/rust/crates/j-law-registry/src/withholding_tax_loader.rs +310 -0
  86. data/vendor/rust/crates/j-law-registry/src/withholding_tax_schema.rs +61 -0
  87. metadata +148 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "minitest/autorun"
5
+ require "open3"
6
+ require "fileutils"
7
+ require "bundler"
8
+ require_relative "../lib/j_law_ruby/build_support"
9
+
10
+ class TestGemspec < Minitest::Test
11
+ GEM_ROOT = File.expand_path("..", __dir__)
12
+
13
+ def test_source_gem_keeps_ruby_platform_and_extension_build
14
+ spec = with_native_library_placeholder { load_gemspec(binary: false) }
15
+
16
+ assert_equal Gem::Platform::RUBY, spec.fetch("platform")
17
+ assert_equal ["ext/j_law_ruby/extconf.rb"], spec.fetch("extensions")
18
+ refute_includes spec.fetch("files"), native_library_path
19
+ end
20
+
21
+ def test_binary_gem_is_platform_specific_and_bundles_native_library
22
+ spec = with_native_library_placeholder { load_gemspec(binary: true) }
23
+
24
+ assert_equal Gem::Platform.local.to_s, spec.fetch("platform")
25
+ assert_empty spec.fetch("extensions")
26
+ assert_includes spec.fetch("files"), native_library_path
27
+ end
28
+
29
+ private
30
+
31
+ def load_gemspec(binary:)
32
+ script = <<~RUBY
33
+ require "json"
34
+
35
+ spec = Gem::Specification.load("j_law_ruby.gemspec")
36
+ abort "failed to load gemspec" if spec.nil?
37
+
38
+ puts JSON.generate(
39
+ platform: spec.platform.to_s,
40
+ extensions: spec.extensions,
41
+ files: spec.files.sort
42
+ )
43
+ RUBY
44
+
45
+ stdout = nil
46
+ stderr = nil
47
+ status = nil
48
+
49
+ Bundler.with_unbundled_env do
50
+ stdout, stderr, status = Open3.capture3(
51
+ { JLawRuby::BuildSupport::BINARY_GEM_ENV => (binary ? "1" : "0") },
52
+ "ruby",
53
+ "-e",
54
+ script,
55
+ chdir: GEM_ROOT
56
+ )
57
+ end
58
+
59
+ assert status.success?, stderr
60
+
61
+ JSON.parse(stdout)
62
+ end
63
+
64
+ def native_library_path
65
+ File.join(
66
+ "lib",
67
+ "j_law_ruby",
68
+ "native",
69
+ JLawRuby::BuildSupport.shared_library_filename
70
+ )
71
+ end
72
+
73
+ def with_native_library_placeholder
74
+ path = File.join(GEM_ROOT, native_library_path)
75
+ created = !File.exist?(path)
76
+ FileUtils.mkdir_p(File.dirname(path))
77
+ File.write(path, "") if created
78
+ yield
79
+ ensure
80
+ FileUtils.rm_f(path) if created
81
+ end
82
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # 所得税法第89条に基づく所得税計算のテスト。
4
+ #
5
+ # 法的根拠: 所得税法 第89条第1項 / 復興財源確保法 第13条
6
+ # テストケースは tests/fixtures/income_tax.json から読み込む。
7
+ #
8
+ # 実行方法:
9
+ # rake compile && ruby -Ilib test/test_income_tax.rb
10
+
11
+ require "minitest/autorun"
12
+ require "json"
13
+ require "date"
14
+ require "j_law_ruby"
15
+
16
+ INCOME_TAX_FIXTURES = JSON.parse(
17
+ File.read(File.join(__dir__, "../../../tests/fixtures/income_tax.json"))
18
+ )
19
+
20
+ # ─── データ駆動テスト ─────────────────────────────────────────────────────────
21
+
22
+ class TestIncomeTaxFixtures < Minitest::Test
23
+ INCOME_TAX_FIXTURES["income_tax"].each do |c|
24
+ define_method("test_#{c['id']}") do
25
+ inp = c["input"]
26
+ exp = c["expected"]
27
+
28
+ date = Date.parse(inp["date"])
29
+ r = JLawRuby::IncomeTax.calc_income_tax(
30
+ inp["taxable_income"], date,
31
+ inp["apply_reconstruction_tax"]
32
+ )
33
+
34
+ assert_equal exp["base_tax"], r.base_tax, "#{c['id']}: base_tax"
35
+ assert_equal exp["reconstruction_tax"], r.reconstruction_tax, "#{c['id']}: reconstruction_tax"
36
+ assert_equal exp["total_tax"], r.total_tax, "#{c['id']}: total_tax"
37
+ if exp["reconstruction_tax_applied"]
38
+ assert r.reconstruction_tax_applied?, "#{c['id']}: reconstruction_tax_applied"
39
+ else
40
+ refute r.reconstruction_tax_applied?, "#{c['id']}: reconstruction_tax_applied"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ # ─── 言語固有テスト ──────────────────────────────────────────────────────────
47
+
48
+ class TestIncomeTaxLanguageSpecific < Minitest::Test
49
+ def test_error_date_out_of_range
50
+ assert_raises(RuntimeError) do
51
+ JLawRuby::IncomeTax.calc_income_tax(5_000_000, Date.new(1988, 12, 31), true)
52
+ end
53
+ end
54
+
55
+ def test_breakdown_fields
56
+ r = JLawRuby::IncomeTax.calc_income_tax(5_000_000, Date.new(2024, 1, 1), true)
57
+ refute_empty r.breakdown
58
+ r.breakdown.each do |step|
59
+ refute_empty step[:label]
60
+ assert_operator step[:rate_denom], :>, 0
61
+ end
62
+ end
63
+
64
+ def test_inspect
65
+ r = JLawRuby::IncomeTax.calc_income_tax(5_000_000, Date.new(2024, 1, 1), true)
66
+ assert_match(/IncomeTaxResult/, r.inspect)
67
+ end
68
+
69
+ def test_type_error_invalid_date
70
+ assert_raises(TypeError) do
71
+ JLawRuby::IncomeTax.calc_income_tax(5_000_000, "2024-01-01", true)
72
+ end
73
+ assert_raises(TypeError) do
74
+ JLawRuby::IncomeTax.calc_income_tax(5_000_000, 20_240_101, true)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "json"
5
+ require "date"
6
+ require "j_law_ruby"
7
+
8
+ FIXTURES = JSON.parse(
9
+ File.read(File.join(__dir__, "../../../tests/fixtures/income_tax_deductions.json"))
10
+ )
11
+
12
+ def build_input(input)
13
+ {
14
+ spouse: input["spouse"] && {
15
+ spouse_total_income_amount: input["spouse"]["spouse_total_income_amount"],
16
+ is_same_household: input["spouse"]["is_same_household"],
17
+ is_elderly: input["spouse"]["is_elderly"],
18
+ },
19
+ dependent: {
20
+ general_count: input.fetch("dependent", {}).fetch("general_count", 0),
21
+ specific_count: input.fetch("dependent", {}).fetch("specific_count", 0),
22
+ elderly_cohabiting_count: input.fetch("dependent", {}).fetch("elderly_cohabiting_count", 0),
23
+ elderly_other_count: input.fetch("dependent", {}).fetch("elderly_other_count", 0),
24
+ },
25
+ social_insurance_premium_paid: input.fetch("social_insurance_premium_paid", 0),
26
+ medical: input["medical"] && {
27
+ medical_expense_paid: input["medical"]["medical_expense_paid"],
28
+ reimbursed_amount: input["medical"]["reimbursed_amount"],
29
+ },
30
+ life_insurance: input["life_insurance"] && {
31
+ new_general_paid_amount: input["life_insurance"]["new_general_paid_amount"],
32
+ new_individual_pension_paid_amount: input["life_insurance"]["new_individual_pension_paid_amount"],
33
+ new_care_medical_paid_amount: input["life_insurance"]["new_care_medical_paid_amount"],
34
+ old_general_paid_amount: input["life_insurance"]["old_general_paid_amount"],
35
+ old_individual_pension_paid_amount: input["life_insurance"]["old_individual_pension_paid_amount"],
36
+ },
37
+ donation: input["donation"] && {
38
+ qualified_donation_amount: input["donation"]["qualified_donation_amount"],
39
+ },
40
+ }
41
+ end
42
+
43
+ class TestIncomeTaxDeductionsFixtures < Minitest::Test
44
+ FIXTURES["income_tax_deductions"].each do |c|
45
+ define_method("test_#{c['id']}") do
46
+ inp = c["input"]
47
+ exp = c["expected"]
48
+
49
+ result = JLawRuby::IncomeTax.calc_income_deductions(
50
+ inp["total_income_amount"],
51
+ Date.parse(inp["date"]),
52
+ **build_input(inp)
53
+ )
54
+
55
+ assert_equal exp["total_income_amount"], result.total_income_amount
56
+ assert_equal exp["total_deductions"], result.total_deductions
57
+ assert_equal exp["taxable_income_before_truncation"], result.taxable_income_before_truncation
58
+ assert_equal exp["taxable_income"], result.taxable_income
59
+ end
60
+ end
61
+ end
62
+
63
+ class TestIncomeTaxAssessmentFixtures < Minitest::Test
64
+ FIXTURES["income_tax_assessment"].each do |c|
65
+ define_method("test_#{c['id']}") do
66
+ inp = c["input"]
67
+ exp = c["expected"]
68
+
69
+ result = JLawRuby::IncomeTax.calc_income_tax_assessment(
70
+ inp["total_income_amount"],
71
+ Date.parse(inp["date"]),
72
+ apply_reconstruction_tax: inp["apply_reconstruction_tax"],
73
+ **build_input(inp)
74
+ )
75
+
76
+ assert_equal exp["taxable_income"], result.deductions.taxable_income
77
+ assert_equal exp["base_tax"], result.tax.base_tax
78
+ assert_equal exp["reconstruction_tax"], result.tax.reconstruction_tax
79
+ assert_equal exp["total_tax"], result.tax.total_tax
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ # 宅建業法第46条に基づく媒介報酬計算のテスト。
4
+ #
5
+ # 法的根拠: 宅地建物取引業法 第46条第1項 / 国土交通省告示
6
+ # テストケースは tests/fixtures/real_estate.json から読み込む。
7
+ #
8
+ # 実行方法:
9
+ # rake compile && ruby -Ilib test/test_real_estate.rb
10
+
11
+ require "minitest/autorun"
12
+ require "json"
13
+ require "date"
14
+ require "j_law_ruby"
15
+
16
+ REAL_ESTATE_FIXTURES = JSON.parse(
17
+ File.read(File.join(__dir__, "../../../tests/fixtures/real_estate.json"))
18
+ )
19
+
20
+ # ─── データ駆動テスト ─────────────────────────────────────────────────────────
21
+
22
+ class TestBrokerageFeeFixtures < Minitest::Test
23
+ REAL_ESTATE_FIXTURES["brokerage_fee"].each do |c|
24
+ define_method("test_#{c['id']}") do
25
+ inp = c["input"]
26
+ exp = c["expected"]
27
+
28
+ date = Date.parse(inp["date"])
29
+ r = JLawRuby::RealEstate.calc_brokerage_fee(
30
+ inp["price"], date,
31
+ inp["is_low_cost_vacant_house"], inp.fetch("is_seller", false)
32
+ )
33
+
34
+ if exp.key?("total_without_tax")
35
+ assert_equal exp["total_without_tax"], r.total_without_tax, "#{c['id']}: total_without_tax"
36
+ end
37
+ if exp.key?("tax_amount")
38
+ assert_equal exp["tax_amount"], r.tax_amount, "#{c['id']}: tax_amount"
39
+ end
40
+ if exp.key?("total_with_tax")
41
+ assert_equal exp["total_with_tax"], r.total_with_tax, "#{c['id']}: total_with_tax"
42
+ end
43
+ if exp.key?("low_cost_special_applied")
44
+ if exp["low_cost_special_applied"]
45
+ assert r.low_cost_special_applied?, "#{c['id']}: low_cost_special_applied"
46
+ else
47
+ refute r.low_cost_special_applied?, "#{c['id']}: low_cost_special_applied"
48
+ end
49
+ end
50
+ if exp.key?("breakdown_results")
51
+ actual = r.breakdown.map { |step| step[:result] }
52
+ assert_equal exp["breakdown_results"], actual, "#{c['id']}: breakdown_results"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # ─── 言語固有テスト ──────────────────────────────────────────────────────────
59
+
60
+ class TestBrokerageFeeLanguageSpecific < Minitest::Test
61
+ def test_error_date_out_of_range
62
+ # 1970-12-01 施行のため、それ以前の日付はエラー
63
+ err = assert_raises(RuntimeError) do
64
+ JLawRuby::RealEstate.calc_brokerage_fee(5_000_000, Date.new(1970, 11, 30), false, false)
65
+ end
66
+ assert_match(/1970-11-30/, err.message)
67
+ end
68
+
69
+ def test_breakdown_fields
70
+ r = JLawRuby::RealEstate.calc_brokerage_fee(5_000_000, Date.new(2024, 8, 1), false, false)
71
+ r.breakdown.each do |step|
72
+ refute_empty step[:label]
73
+ assert_operator step[:rate_denom], :>, 0
74
+ end
75
+ end
76
+
77
+ def test_inspect
78
+ r = JLawRuby::RealEstate.calc_brokerage_fee(5_000_000, Date.new(2024, 8, 1), false, false)
79
+ assert_match(/BrokerageFeeResult/, r.inspect)
80
+ end
81
+
82
+ def test_type_error_invalid_date
83
+ assert_raises(TypeError) do
84
+ JLawRuby::RealEstate.calc_brokerage_fee(5_000_000, "2024-08-01", false, false)
85
+ end
86
+ assert_raises(TypeError) do
87
+ JLawRuby::RealEstate.calc_brokerage_fee(5_000_000, 20_240_801, false, false)
88
+ end
89
+ end
90
+
91
+ def test_negative_price_is_rejected
92
+ error = assert_raises(ArgumentError) do
93
+ JLawRuby::RealEstate.calc_brokerage_fee(-1, Date.new(2024, 8, 1), false, false)
94
+ end
95
+
96
+ assert_match(/price/, error.message)
97
+ end
98
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "json"
5
+ require "date"
6
+ require "j_law_ruby"
7
+
8
+ class TestStampTax < Minitest::Test
9
+ FIXTURES = JSON.parse(File.read(File.join(__dir__, "../../../tests/fixtures/stamp_tax.json")))
10
+
11
+ FIXTURES["stamp_tax"].each do |tc|
12
+ define_method("test_#{tc['id']}") do
13
+ inp = tc["input"]
14
+ exp = tc["expected"]
15
+
16
+ date = Date.parse(inp["date"])
17
+ result = JLawRuby::StampTax.calc_stamp_tax(
18
+ inp["document_code"],
19
+ inp["stated_amount"],
20
+ date,
21
+ flags: inp["flags"]
22
+ )
23
+
24
+ assert_equal exp["tax_amount"], result.tax_amount, "#{tc['id']}: tax_amount"
25
+ assert_equal exp["rule_label"], result.rule_label, "#{tc['id']}: rule_label"
26
+ if exp["applied_special_rule"].nil?
27
+ assert_nil result.applied_special_rule, "#{tc['id']}: applied_special_rule"
28
+ else
29
+ assert_equal exp["applied_special_rule"], result.applied_special_rule, "#{tc['id']}: applied_special_rule"
30
+ end
31
+ end
32
+ end
33
+
34
+ def test_error_date_out_of_range
35
+ err = assert_raises(RuntimeError) do
36
+ JLawRuby::StampTax.calc_stamp_tax("article1_real_estate_transfer", 5_000_000, Date.new(2014, 3, 31))
37
+ end
38
+ assert_match(/2014-03-31/, err.message)
39
+ end
40
+
41
+ def test_inspect
42
+ result = JLawRuby::StampTax.calc_stamp_tax("article1_real_estate_transfer", 5_000_000, Date.new(2024, 8, 1))
43
+ assert_match(/StampTaxResult/, result.inspect)
44
+ end
45
+
46
+ def test_type_error_invalid_date
47
+ assert_raises(TypeError) do
48
+ JLawRuby::StampTax.calc_stamp_tax("article1_real_estate_transfer", 5_000_000, "2024-08-01")
49
+ end
50
+ end
51
+
52
+ def test_invalid_document_code
53
+ assert_raises(ArgumentError) do
54
+ JLawRuby::StampTax.calc_stamp_tax("invalid_code", 5_000_000, Date.new(2024, 8, 1))
55
+ end
56
+ end
57
+
58
+ def test_invalid_flag
59
+ assert_raises(ArgumentError) do
60
+ JLawRuby::StampTax.calc_stamp_tax(
61
+ "article17_sales_receipt",
62
+ 70_000,
63
+ Date.new(2024, 8, 1),
64
+ flags: ["invalid_flag"]
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "json"
5
+ require "date"
6
+ require "j_law_ruby"
7
+
8
+ class TestWithholdingTax < Minitest::Test
9
+ FIXTURES = JSON.parse(File.read(File.join(__dir__, "../../../tests/fixtures/withholding_tax.json")))
10
+
11
+ FIXTURES["withholding_tax"].each do |tc|
12
+ define_method("test_#{tc['id']}") do
13
+ inp = tc["input"]
14
+ exp = tc["expected"]
15
+
16
+ result = JLawRuby::WithholdingTax.calc_withholding_tax(
17
+ inp["payment_amount"],
18
+ Date.parse(inp["date"]),
19
+ inp["category"],
20
+ is_submission_prize: inp["is_submission_prize"],
21
+ separated_consumption_tax_amount: inp["separated_consumption_tax_amount"]
22
+ )
23
+
24
+ assert_equal exp["taxable_payment_amount"], result.taxable_payment_amount
25
+ assert_equal exp["tax_amount"], result.tax_amount
26
+ assert_equal exp["net_payment_amount"], result.net_payment_amount
27
+ assert_equal exp["submission_prize_exempted"], result.submission_prize_exempted?
28
+ end
29
+ end
30
+
31
+ def test_out_of_range_date
32
+ assert_raises(RuntimeError) do
33
+ JLawRuby::WithholdingTax.calc_withholding_tax(
34
+ 100_000,
35
+ Date.new(2012, 12, 31),
36
+ :manuscript_and_lecture
37
+ )
38
+ end
39
+ end
40
+
41
+ def test_breakdown_fields
42
+ result = JLawRuby::WithholdingTax.calc_withholding_tax(
43
+ 1_500_000,
44
+ Date.new(2026, 1, 1),
45
+ :professional_fee
46
+ )
47
+ assert_equal 2, result.breakdown.length
48
+ refute_empty result.breakdown.first[:label]
49
+ end
50
+
51
+ def test_inspect
52
+ result = JLawRuby::WithholdingTax.calc_withholding_tax(
53
+ 100_000,
54
+ Date.new(2026, 1, 1),
55
+ :manuscript_and_lecture
56
+ )
57
+ assert_match(/WithholdingTaxResult/, result.inspect)
58
+ end
59
+
60
+ def test_type_error_invalid_date
61
+ assert_raises(TypeError) do
62
+ JLawRuby::WithholdingTax.calc_withholding_tax(100_000, "2026-01-01", :professional_fee)
63
+ end
64
+ end
65
+ end