focus_nfe 1.0.0

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 (206) hide show
  1. checksums.yaml +7 -0
  2. data/.git-hooks/pre_push/steep.rb +18 -0
  3. data/.git-hooks/pre_push/yard_doc.rb +18 -0
  4. data/.gitattributes +1 -0
  5. data/.overcommit.yml +69 -0
  6. data/.rspec +3 -0
  7. data/.yardopts +11 -0
  8. data/CHANGELOG.md +77 -0
  9. data/CLAUDE.md +118 -0
  10. data/CODE_OF_CONDUCT.md +10 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +348 -0
  13. data/Rakefile +105 -0
  14. data/data/schemas/schema_cte.json +2793 -0
  15. data/data/schemas/schema_cte_os.json +1335 -0
  16. data/data/schemas/schema_cte_os_transporte_rodoviario.json +109 -0
  17. data/data/schemas/schema_cte_transporte_aereo.json +115 -0
  18. data/data/schemas/schema_cte_transporte_aquaviario.json +174 -0
  19. data/data/schemas/schema_cte_transporte_dutoviario.json +65 -0
  20. data/data/schemas/schema_cte_transporte_ferroviario.json +144 -0
  21. data/data/schemas/schema_cte_transporte_multimodal.json +45 -0
  22. data/data/schemas/schema_cte_transporte_rodoviario.json +78 -0
  23. data/data/schemas/schema_dce.json +549 -0
  24. data/data/schemas/schema_mdfe.json +1102 -0
  25. data/data/schemas/schema_mdfe_transporte_aereo.json +44 -0
  26. data/data/schemas/schema_mdfe_transporte_aquaviario.json +209 -0
  27. data/data/schemas/schema_mdfe_transporte_ferroviario.json +99 -0
  28. data/data/schemas/schema_mdfe_transporte_rodoviario.json +628 -0
  29. data/data/schemas/schema_nfcom.json +1859 -0
  30. data/data/schemas/schema_nfe.json +4750 -0
  31. data/data/schemas/schema_nfe_forma_pagamento.json +97 -0
  32. data/data/schemas/schema_nfe_item.json +2574 -0
  33. data/data/schemas/schema_nfgas.json +2316 -0
  34. data/data/schemas/schema_nfse_nacional.json +1847 -0
  35. data/data/schemas/schema_nfse_recebida.json +548 -0
  36. data/lib/focus_nfe/client.rb +162 -0
  37. data/lib/focus_nfe/configuration.rb +104 -0
  38. data/lib/focus_nfe/errors.rb +123 -0
  39. data/lib/focus_nfe/esquemas/campo.rb +171 -0
  40. data/lib/focus_nfe/esquemas/catalogo.rb +34 -0
  41. data/lib/focus_nfe/esquemas/decimal.rb +66 -0
  42. data/lib/focus_nfe/esquemas/esquema.rb +72 -0
  43. data/lib/focus_nfe/esquemas/validador.rb +87 -0
  44. data/lib/focus_nfe/http/adapter.rb +25 -0
  45. data/lib/focus_nfe/http/adapters/net_http.rb +99 -0
  46. data/lib/focus_nfe/http/authentication.rb +23 -0
  47. data/lib/focus_nfe/http/connection.rb +118 -0
  48. data/lib/focus_nfe/http/logging.rb +100 -0
  49. data/lib/focus_nfe/http/response.rb +75 -0
  50. data/lib/focus_nfe/modelos/documento.rb +113 -0
  51. data/lib/focus_nfe/modelos/inutilizacao.rb +75 -0
  52. data/lib/focus_nfe/modelos/pagina.rb +54 -0
  53. data/lib/focus_nfe/recursos/backups.rb +17 -0
  54. data/lib/focus_nfe/recursos/base.rb +91 -0
  55. data/lib/focus_nfe/recursos/ceps.rb +16 -0
  56. data/lib/focus_nfe/recursos/cfops.rb +16 -0
  57. data/lib/focus_nfe/recursos/cnaes.rb +16 -0
  58. data/lib/focus_nfe/recursos/cnpjs.rb +13 -0
  59. data/lib/focus_nfe/recursos/concerns/baixavel.rb +41 -0
  60. data/lib/focus_nfe/recursos/concerns/baixavel_eventos.rb +34 -0
  61. data/lib/focus_nfe/recursos/concerns/cancelavel.rb +25 -0
  62. data/lib/focus_nfe/recursos/concerns/conciliavel.rb +66 -0
  63. data/lib/focus_nfe/recursos/concerns/consultavel.rb +26 -0
  64. data/lib/focus_nfe/recursos/concerns/corrigivel.rb +45 -0
  65. data/lib/focus_nfe/recursos/concerns/corrigivel_cte.rb +60 -0
  66. data/lib/focus_nfe/recursos/concerns/emitivel.rb +51 -0
  67. data/lib/focus_nfe/recursos/concerns/enviavel.rb +40 -0
  68. data/lib/focus_nfe/recursos/concerns/eventavel.rb +46 -0
  69. data/lib/focus_nfe/recursos/concerns/inutilizavel.rb +96 -0
  70. data/lib/focus_nfe/recursos/concerns/listavel.rb +22 -0
  71. data/lib/focus_nfe/recursos/concerns/localizavel.rb +22 -0
  72. data/lib/focus_nfe/recursos/concerns/notificavel.rb +23 -0
  73. data/lib/focus_nfe/recursos/concerns/removivel.rb +20 -0
  74. data/lib/focus_nfe/recursos/concerns/visualizavel.rb +28 -0
  75. data/lib/focus_nfe/recursos/cte.rb +35 -0
  76. data/lib/focus_nfe/recursos/cte_os.rb +29 -0
  77. data/lib/focus_nfe/recursos/ctes_recebidas.rb +38 -0
  78. data/lib/focus_nfe/recursos/dce.rb +16 -0
  79. data/lib/focus_nfe/recursos/emails_bloqueados.rb +31 -0
  80. data/lib/focus_nfe/recursos/empresas.rb +35 -0
  81. data/lib/focus_nfe/recursos/mdfe.rb +78 -0
  82. data/lib/focus_nfe/recursos/municipios.rb +60 -0
  83. data/lib/focus_nfe/recursos/ncms.rb +16 -0
  84. data/lib/focus_nfe/recursos/nfce.rb +19 -0
  85. data/lib/focus_nfe/recursos/nfcom.rb +16 -0
  86. data/lib/focus_nfe/recursos/nfe.rb +106 -0
  87. data/lib/focus_nfe/recursos/nfes_recebidas.rb +56 -0
  88. data/lib/focus_nfe/recursos/nfgas.rb +16 -0
  89. data/lib/focus_nfe/recursos/nfse.rb +18 -0
  90. data/lib/focus_nfe/recursos/nfse_nacional.rb +16 -0
  91. data/lib/focus_nfe/recursos/nfses_nacionais_recebidas.rb +21 -0
  92. data/lib/focus_nfe/recursos/webhooks.rb +22 -0
  93. data/lib/focus_nfe/version.rb +6 -0
  94. data/lib/focus_nfe/webhook.rb +68 -0
  95. data/lib/focus_nfe.rb +124 -0
  96. data/sig/focus_nfe/client.rbs +38 -0
  97. data/sig/focus_nfe/configuration.rbs +29 -0
  98. data/sig/focus_nfe/errors.rbs +59 -0
  99. data/sig/focus_nfe/esquemas/campo.rbs +47 -0
  100. data/sig/focus_nfe/esquemas/catalogo.rbs +8 -0
  101. data/sig/focus_nfe/esquemas/decimal.rbs +25 -0
  102. data/sig/focus_nfe/esquemas/esquema.rbs +30 -0
  103. data/sig/focus_nfe/esquemas/validador.rbs +20 -0
  104. data/sig/focus_nfe/http/adapter.rbs +7 -0
  105. data/sig/focus_nfe/http/adapters/net_http.rbs +25 -0
  106. data/sig/focus_nfe/http/authentication.rbs +9 -0
  107. data/sig/focus_nfe/http/connection.rbs +32 -0
  108. data/sig/focus_nfe/http/logging.rbs +30 -0
  109. data/sig/focus_nfe/http/response.rbs +28 -0
  110. data/sig/focus_nfe/modelos/documento.rbs +34 -0
  111. data/sig/focus_nfe/modelos/inutilizacao.rbs +24 -0
  112. data/sig/focus_nfe/modelos/pagina.rbs +21 -0
  113. data/sig/focus_nfe/recursos/backups.rbs +7 -0
  114. data/sig/focus_nfe/recursos/base.rbs +25 -0
  115. data/sig/focus_nfe/recursos/ceps.rbs +10 -0
  116. data/sig/focus_nfe/recursos/cfops.rbs +10 -0
  117. data/sig/focus_nfe/recursos/cnaes.rbs +10 -0
  118. data/sig/focus_nfe/recursos/cnpjs.rbs +7 -0
  119. data/sig/focus_nfe/recursos/concerns/baixavel.rbs +12 -0
  120. data/sig/focus_nfe/recursos/concerns/baixavel_eventos.rbs +14 -0
  121. data/sig/focus_nfe/recursos/concerns/cancelavel.rbs +9 -0
  122. data/sig/focus_nfe/recursos/concerns/conciliavel.rbs +15 -0
  123. data/sig/focus_nfe/recursos/concerns/consultavel.rbs +9 -0
  124. data/sig/focus_nfe/recursos/concerns/corrigivel.rbs +15 -0
  125. data/sig/focus_nfe/recursos/concerns/corrigivel_cte.rbs +17 -0
  126. data/sig/focus_nfe/recursos/concerns/emitivel.rbs +14 -0
  127. data/sig/focus_nfe/recursos/concerns/enviavel.rbs +15 -0
  128. data/sig/focus_nfe/recursos/concerns/eventavel.rbs +12 -0
  129. data/sig/focus_nfe/recursos/concerns/inutilizavel.rbs +20 -0
  130. data/sig/focus_nfe/recursos/concerns/listavel.rbs +9 -0
  131. data/sig/focus_nfe/recursos/concerns/localizavel.rbs +9 -0
  132. data/sig/focus_nfe/recursos/concerns/notificavel.rbs +9 -0
  133. data/sig/focus_nfe/recursos/concerns/removivel.rbs +9 -0
  134. data/sig/focus_nfe/recursos/concerns/visualizavel.rbs +9 -0
  135. data/sig/focus_nfe/recursos/cte.rbs +17 -0
  136. data/sig/focus_nfe/recursos/cte_os.rbs +17 -0
  137. data/sig/focus_nfe/recursos/ctes_recebidas.rbs +14 -0
  138. data/sig/focus_nfe/recursos/dce.rbs +10 -0
  139. data/sig/focus_nfe/recursos/emails_bloqueados.rbs +12 -0
  140. data/sig/focus_nfe/recursos/empresas.rbs +12 -0
  141. data/sig/focus_nfe/recursos/mdfe.rbs +21 -0
  142. data/sig/focus_nfe/recursos/municipios.rbs +19 -0
  143. data/sig/focus_nfe/recursos/ncms.rbs +10 -0
  144. data/sig/focus_nfe/recursos/nfce.rbs +12 -0
  145. data/sig/focus_nfe/recursos/nfcom.rbs +10 -0
  146. data/sig/focus_nfe/recursos/nfe.rbs +23 -0
  147. data/sig/focus_nfe/recursos/nfes_recebidas.rbs +16 -0
  148. data/sig/focus_nfe/recursos/nfgas.rbs +10 -0
  149. data/sig/focus_nfe/recursos/nfse.rbs +11 -0
  150. data/sig/focus_nfe/recursos/nfse_nacional.rbs +10 -0
  151. data/sig/focus_nfe/recursos/nfses_nacionais_recebidas.rbs +11 -0
  152. data/sig/focus_nfe/recursos/webhooks.rbs +11 -0
  153. data/sig/focus_nfe/webhook.rbs +11 -0
  154. data/sig/focus_nfe.rbs +10 -0
  155. data/spec/focus_nfe/client_spec.rb +208 -0
  156. data/spec/focus_nfe/configuration_spec.rb +121 -0
  157. data/spec/focus_nfe/errors_mapping_spec.rb +68 -0
  158. data/spec/focus_nfe/errors_spec.rb +107 -0
  159. data/spec/focus_nfe/esquemas/campo_spec.rb +291 -0
  160. data/spec/focus_nfe/esquemas/decimal_spec.rb +54 -0
  161. data/spec/focus_nfe/esquemas/esquema_spec.rb +73 -0
  162. data/spec/focus_nfe/esquemas/validador_spec.rb +167 -0
  163. data/spec/focus_nfe/esquemas_spec.rb +42 -0
  164. data/spec/focus_nfe/http/adapter_spec.rb +8 -0
  165. data/spec/focus_nfe/http/adapters/net_http_spec.rb +181 -0
  166. data/spec/focus_nfe/http/authentication_spec.rb +24 -0
  167. data/spec/focus_nfe/http/connection_spec.rb +255 -0
  168. data/spec/focus_nfe/http/logging_spec.rb +83 -0
  169. data/spec/focus_nfe/http/response_spec.rb +161 -0
  170. data/spec/focus_nfe/modelos/documento_spec.rb +150 -0
  171. data/spec/focus_nfe/modelos/inutilizacao_spec.rb +91 -0
  172. data/spec/focus_nfe/modelos/pagina_spec.rb +77 -0
  173. data/spec/focus_nfe/recursos/backups_spec.rb +29 -0
  174. data/spec/focus_nfe/recursos/base_spec.rb +56 -0
  175. data/spec/focus_nfe/recursos/ceps_spec.rb +16 -0
  176. data/spec/focus_nfe/recursos/cfops_spec.rb +16 -0
  177. data/spec/focus_nfe/recursos/cnaes_spec.rb +20 -0
  178. data/spec/focus_nfe/recursos/cnpjs_spec.rb +11 -0
  179. data/spec/focus_nfe/recursos/concerns/emitivel_validacao_spec.rb +158 -0
  180. data/spec/focus_nfe/recursos/cte_os_spec.rb +9 -0
  181. data/spec/focus_nfe/recursos/cte_spec.rb +9 -0
  182. data/spec/focus_nfe/recursos/ctes_recebidas_spec.rb +56 -0
  183. data/spec/focus_nfe/recursos/dce_spec.rb +8 -0
  184. data/spec/focus_nfe/recursos/emails_bloqueados_spec.rb +29 -0
  185. data/spec/focus_nfe/recursos/empresas_spec.rb +45 -0
  186. data/spec/focus_nfe/recursos/mdfe_spec.rb +100 -0
  187. data/spec/focus_nfe/recursos/municipios_spec.rb +58 -0
  188. data/spec/focus_nfe/recursos/ncms_spec.rb +16 -0
  189. data/spec/focus_nfe/recursos/nfce_spec.rb +10 -0
  190. data/spec/focus_nfe/recursos/nfcom_spec.rb +8 -0
  191. data/spec/focus_nfe/recursos/nfe_spec.rb +262 -0
  192. data/spec/focus_nfe/recursos/nfes_recebidas_spec.rb +87 -0
  193. data/spec/focus_nfe/recursos/nfgas_spec.rb +8 -0
  194. data/spec/focus_nfe/recursos/nfse_nacional_spec.rb +8 -0
  195. data/spec/focus_nfe/recursos/nfse_spec.rb +9 -0
  196. data/spec/focus_nfe/recursos/nfses_nacionais_recebidas_spec.rb +17 -0
  197. data/spec/focus_nfe/recursos/webhooks_spec.rb +22 -0
  198. data/spec/focus_nfe/webhook_spec.rb +66 -0
  199. data/spec/focus_nfe_global_configuration_spec.rb +70 -0
  200. data/spec/focus_nfe_require_spec.rb +87 -0
  201. data/spec/focus_nfe_spec.rb +11 -0
  202. data/spec/spec_helper.rb +58 -0
  203. data/spec/support/shared_examples/recurso_fiscal.rb +445 -0
  204. data/spec/support/shared_examples/recurso_leitura.rb +217 -0
  205. data/tools/pull_fields.rb +62 -0
  206. metadata +420 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe FocusNfe::Webhook do
4
+ describe ".parse" do
5
+ it "parseia o corpo cru (String) em um Documento", :aggregate_failures do
6
+ doc = described_class.parse('{"status":"autorizado","ref":"pedido-1"}')
7
+
8
+ expect(doc).to be_a(FocusNfe::Modelos::Documento)
9
+ expect(doc).to be_autorizado
10
+ expect(doc.ref).to eq("pedido-1")
11
+ end
12
+
13
+ it "aceita um Hash já parseado" do
14
+ doc = described_class.parse({ "status" => "cancelado", "ref" => "pedido-2" })
15
+
16
+ expect(doc).to be_cancelado
17
+ end
18
+
19
+ it "injeta a ref informada quando o corpo não a traz" do
20
+ doc = described_class.parse('{"status":"autorizado"}', ref: "pedido-3")
21
+
22
+ expect(doc.ref).to eq("pedido-3")
23
+ end
24
+
25
+ it "não guarda resposta HTTP" do
26
+ expect(described_class.parse('{"status":"autorizado"}').response).to be_nil
27
+ end
28
+
29
+ it "levanta WebhookError quando o corpo não é JSON válido" do
30
+ expect { described_class.parse("nao é json") }
31
+ .to raise_error(FocusNfe::Errors::WebhookError, /não é JSON válido/)
32
+ end
33
+ end
34
+
35
+ describe ".autenticado?" do
36
+ let(:headers) { { "X-Focus-Authorization" => "s3cr3t" } }
37
+
38
+ def autenticado?(headers:, authorization: "s3cr3t", authorization_header: "X-Focus-Authorization")
39
+ described_class.autenticado?(headers: headers, authorization: authorization,
40
+ authorization_header: authorization_header)
41
+ end
42
+
43
+ it "é verdadeiro quando o header recebido bate com o authorization" do
44
+ expect(autenticado?(headers: headers)).to be(true)
45
+ end
46
+
47
+ it "é falso quando o valor do header diverge" do
48
+ expect(autenticado?(headers: { "X-Focus-Authorization" => "errado" })).to be(false)
49
+ end
50
+
51
+ it "é falso quando o header está ausente" do
52
+ expect(autenticado?(headers: {})).to be(false)
53
+ end
54
+
55
+ it "faz o lookup do header de forma case-insensitive" do
56
+ expect(autenticado?(headers: { "x-focus-authorization" => "s3cr3t" })).to be(true)
57
+ end
58
+
59
+ it "encontra o header via #[] em objetos sem #each (ex.: request.headers)" do
60
+ rails_like = Object.new
61
+ def rails_like.[](nome) = nome == "X-Focus-Authorization" ? "s3cr3t" : nil
62
+
63
+ expect(autenticado?(headers: rails_like)).to be(true)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe FocusNfe do
4
+ describe ".configure" do
5
+ it "cede uma Configuration para o bloco" do
6
+ cedida = nil
7
+ described_class.configure { |config| cedida = config }
8
+
9
+ expect(cedida).to be_a(FocusNfe::Configuration)
10
+ end
11
+
12
+ it "memoiza a configuração ajustada no bloco", :aggregate_failures do
13
+ described_class.configure do |config|
14
+ config.token_empresa = "tok-global"
15
+ config.environment = :producao
16
+ end
17
+
18
+ expect(described_class.configuration).to have_attributes(token_empresa: "tok-global", environment: :producao)
19
+ end
20
+
21
+ it "devolve a configuração global mesmo quando chamado sem bloco" do
22
+ expect(described_class.configure).to be(described_class.configuration)
23
+ end
24
+ end
25
+
26
+ describe ".configuration" do
27
+ it "cria uma Configuration default quando ainda não há" do
28
+ expect(described_class.configuration).to be_a(FocusNfe::Configuration)
29
+ end
30
+
31
+ it "devolve sempre a mesma instância global" do
32
+ primeira = described_class.configuration
33
+
34
+ expect(described_class.configuration).to be(primeira)
35
+ end
36
+ end
37
+
38
+ describe ".client" do
39
+ it "constrói um Client a partir da config global" do
40
+ described_class.configure { |config| config.token_empresa = "tok-global" }
41
+
42
+ expect(described_class.client).to be_a(FocusNfe::Client)
43
+ end
44
+
45
+ it "usa a config global no cliente" do
46
+ described_class.configure { |config| config.token_empresa = "tok-global" }
47
+
48
+ expect(described_class.client.configuration).to be(described_class.configuration)
49
+ end
50
+
51
+ it "levanta ConfigurationError quando a config global ainda não tem nenhum token" do
52
+ expect { described_class.client }.to raise_error(FocusNfe::Errors::ConfigurationError)
53
+ end
54
+ end
55
+
56
+ describe ".reset_configuration!" do
57
+ it "limpa o estado global, gerando uma nova instância depois" do
58
+ anterior = described_class.configuration
59
+ described_class.reset_configuration!
60
+
61
+ expect(described_class.configuration).not_to be(anterior)
62
+ end
63
+ end
64
+
65
+ describe "isolamento entre exemplos" do
66
+ it "não vê o token de um exemplo anterior" do
67
+ expect(described_class.configuration.token_empresa).to be_nil
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ RSpec.describe FocusNfe do
6
+ describe "require \"focus_nfe\" em processo limpo" do
7
+ def constantes
8
+ %w[
9
+ FocusNfe::Client
10
+ FocusNfe::Configuration
11
+ FocusNfe::HTTP::Connection
12
+ FocusNfe::Error
13
+ FocusNfe::Errors::HttpError
14
+ FocusNfe::Errors::ConfigurationError
15
+ FocusNfe::Errors::ConnectionError
16
+ ]
17
+ end
18
+
19
+ def executar_ruby(corpo)
20
+ lib = File.expand_path("../lib", __dir__)
21
+ env = ENV.to_h
22
+ %w[RUBYOPT BUNDLE_GEMFILE BUNDLER_SETUP].each { |chave| env.delete(chave) }
23
+
24
+ Open3.capture2e(env, RbConfig.ruby, "--disable-gems", "-I#{lib}", "-e", corpo)
25
+ end
26
+
27
+ it "carrega sem levantar", :aggregate_failures do
28
+ saida, status = executar_ruby('require "focus_nfe"; print "ok"')
29
+
30
+ expect(status).to be_success
31
+ expect(saida).to eq("ok")
32
+ end
33
+
34
+ it "expõe Client, Configuration, HTTP::Connection, Error e Errors::*" do
35
+ checagem = constantes.map { |const| %(defined?(#{const}) || abort("faltou #{const}")) }.join("; ")
36
+ saida, status = executar_ruby(%(require "focus_nfe"; #{checagem}; print "ok"))
37
+
38
+ expect(status).to be_success, saida
39
+ end
40
+
41
+ it "não puxa a lib base64 transitivamente" do
42
+ saida, = executar_ruby('require "focus_nfe"; print $LOADED_FEATURES.grep(/base64/).inspect')
43
+
44
+ expect(saida).to eq("[]")
45
+ end
46
+
47
+ it "monta o Authorization sem depender de base64", :aggregate_failures do
48
+ corpo = 'require "focus_nfe"; print FocusNfe::HTTP::Authentication.header("x").fetch("Authorization")'
49
+ saida, status = executar_ruby(corpo)
50
+
51
+ expect(status).to be_success, saida
52
+ expect(saida).to eq("Basic eDo=")
53
+ end
54
+
55
+ def carregados(corpo)
56
+ executar_ruby(%(require "focus_nfe"; #{corpo}; print $LOADED_FEATURES.grep(%r{focus_nfe/}).join("\\n"))).first
57
+ end
58
+
59
+ it "não carrega nenhum recurso de documento só no require" do
60
+ expect(carregados("")).not_to match(%r{focus_nfe/recursos/})
61
+ end
62
+
63
+ it "não carrega os modelos só no require" do
64
+ expect(carregados("")).not_to match(%r{focus_nfe/modelos/})
65
+ end
66
+
67
+ it "não carrega a máquina de validação de esquemas só no require" do
68
+ expect(carregados("")).not_to match(%r{focus_nfe/esquemas/(esquema|campo|validador)\.rb})
69
+ end
70
+
71
+ it "carrega só o recurso referenciado sob demanda, e suas dependências", :aggregate_failures do
72
+ saida = carregados("FocusNfe::Recursos::NfseNacional")
73
+
74
+ expect(saida).to match(%r{focus_nfe/recursos/nfse_nacional\.rb})
75
+ expect(saida).to match(%r{focus_nfe/recursos/base\.rb})
76
+ expect(saida).to match(%r{focus_nfe/recursos/concerns/emitivel\.rb})
77
+ end
78
+
79
+ it "não puxa recursos irmãos nem os modelos ao referenciar um recurso", :aggregate_failures do
80
+ saida = carregados("FocusNfe::Recursos::NfseNacional")
81
+
82
+ expect(saida).not_to match(%r{focus_nfe/recursos/cte\.rb})
83
+ expect(saida).not_to match(%r{focus_nfe/recursos/nfe\.rb})
84
+ expect(saida).not_to match(%r{focus_nfe/modelos/documento\.rb})
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe FocusNfe do
4
+ it "possui um número de versão" do
5
+ expect(FocusNfe::VERSION).not_to be_nil
6
+ end
7
+
8
+ it "define uma classe de erro base" do
9
+ expect(FocusNfe::Error.new).to be_a(StandardError)
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov"
4
+
5
+ SimpleCov.start do
6
+ enable_coverage :branch
7
+
8
+ add_filter "/spec/"
9
+ add_filter "/tools/"
10
+ add_filter "lib/focus_nfe/version.rb"
11
+
12
+ add_group "Recursos", "lib/focus_nfe/recursos"
13
+ add_group "Esquemas", "lib/focus_nfe/esquemas"
14
+ add_group "HTTP", "lib/focus_nfe/http"
15
+ add_group "Modelos", "lib/focus_nfe/modelos"
16
+ end
17
+
18
+ require "focus_nfe"
19
+
20
+ require "webmock/rspec"
21
+ require "vcr"
22
+
23
+ # Nenhuma requisição HTTP real é permitida durante os testes (ver CLAUDE.md).
24
+ WebMock.disable_net_connect!(allow_localhost: false)
25
+
26
+ VCR.configure do |config|
27
+ config.cassette_library_dir = "spec/cassettes"
28
+ config.hook_into :webmock
29
+ config.configure_rspec_metadata!
30
+ config.default_cassette_options = { record: :none }
31
+ end
32
+
33
+ Dir[File.join(__dir__, "support", "**", "*.rb")].each { |file| require file }
34
+
35
+ RSpec.configure do |config|
36
+ # Habilita a sintaxe `expect` apenas.
37
+ config.expect_with :rspec do |expectations|
38
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
39
+ end
40
+
41
+ config.mock_with :rspec do |mocks|
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # Permite focar exemplos com `fit`/`fdescribe` e roda tudo se nenhum estiver focado.
46
+ config.filter_run_when_matching :focus
47
+
48
+ config.shared_context_metadata_behavior = :apply_to_host_groups
49
+ config.disable_monkey_patching!
50
+ config.warnings = true
51
+
52
+ config.default_formatter = "doc" if config.files_to_run.one?
53
+
54
+ config.order = :random
55
+ Kernel.srand config.seed
56
+
57
+ config.before { FocusNfe.reset_configuration! }
58
+ end