Repository: spf13/viper (go)

Issue:
ReadConfig(in io.Reader) does not Actually ReadConfig
### Preflight Checklist

- [X] I have searched the [issue tracker](https://www.github.com/spf13/viper/issues) for an issue that matches the one I want to file, without success.
- [X] I am not looking for support or already pursued the available [support channels](https://github.com/spf13/viper/issues/new/choose) without success.
- [X] I have checked the [troubleshooting guide](https://github.com/spf13/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.

### Viper Version

1.18.2

### Go Version

1.22

### Config Source

Files

### Format

TOML

### Repl.it link

_No response_

### Code reproducing the issue

```go
/**
testdata/good_config.toml is a good toml file.
Viper sees it initially
**/
f, err := os.Open("testdata/good_config.toml")
if err != nil {
   assert.Fail(suite.T(), err.Error())
}
err = viper.ReadConfig(f)
if err != nil {
  assert.Fail(suite.T(), err.Error())
}
```


### Expected Behavior

should read the config.  When I print the config I expect the contents of the initial file.  but I get {}

### Actual Behavior

Return an empty config

### Steps To Reproduce

_No response_

### Additional Information

Did some initial debugging on this.

![shot1](https://github.com/spf13/viper/assets/5685054/421544d2-609f-4d74-a242-fff01c73100f)
At this point all is well.

![shot2](https://github.com/spf13/viper/assets/5685054/5fd1a2d3-fb02-4deb-868b-93c1665ff206)
Here is where things start to hinky

![shot3](https://github.com/spf13/viper/assets/5685054/1b6c99fe-dbbf-4a0f-b8d1-8e9a01e3bc10)
Still dont have a file tto read? Ok says Viper Ill try to find the config file . . . But wait!

![shot4](https://github.com/spf13/viper/assets/5685054/f900dd87-1be4-4352-9599-252e3450abc2)
Here you can see that configPath has never been set. It still has not set the configFile or pconfigPath despite asking for a file reader that has the name and path of the file. The reult is that it cant find the config file and fails.

BUT!!!
This code works:
```go
file := "testdata/good_config.toml"
viper.SetConfigName(strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)))
viper.SetConfigType("toml")
viper.AddConfigPath(filepath.Dir(file))
f, err := os.Open(file)
if err != nil {
   assert.Fail(suite.T(), err.Error())
}
err = viper.ReadConfig(f)
if err != nil {
  assert.Fail(suite.T(), err.Error())
}
```

`So why would I go through all that trouble to use `ReadConfig(f) ` when I could just go through the same trouble and use ReadInConfig()`?

Seems like `ReadConfig(f)` should just ReadConfig from `f`



Pull request description:
Fix missing config type when reading from a buffer
Return an error from `ReadConfig` and `MergeConfig` when no config type is set.

Fixes #1784

Patch:
diff --git a/viper.go b/viper.go
index 3f9d164..24e9d68 100644
--- a/viper.go
+++ b/viper.go
@@ -1577,6 +1577,10 @@ func (v *Viper) MergeInConfig() error {
 func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
 
 func (v *Viper) ReadConfig(in io.Reader) error {
+	if v.configType == "" {
+		return errors.New("cannot decode configuration: config type is not set")
+	}
+
 	v.config = make(map[string]any)
 	return v.unmarshalReader(in, v.config)
 }
@@ -1585,6 +1589,10 @@ func (v *Viper) ReadConfig(in io.Reader) error {
 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
 
 func (v *Viper) MergeConfig(in io.Reader) error {
+	if v.configType == "" {
+		return errors.New("cannot decode configuration: config type is not set")
+	}
+
 	cfg := make(map[string]any)
 	if err := v.unmarshalReader(in, cfg); err != nil {
 		return err


Test patch:
diff --git a/viper_test.go b/viper_test.go
index 184f0b3..0b9d9c6 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -1641,20 +1641,29 @@ func TestFindsNestedKeys(t *testing.T) {
 	}
 }
 
-func TestReadBufConfig(t *testing.T) {
-	v := New()
-	v.SetConfigType("yaml")
-	v.ReadConfig(bytes.NewBuffer(yamlExample))
-	t.Log(v.AllKeys())
+func TestReadConfig(t *testing.T) {
+	t.Run("ok", func(t *testing.T) {
+		v := New()
+		v.SetConfigType("yaml")
+		err := v.ReadConfig(bytes.NewBuffer(yamlExample))
+		require.NoError(t, err)
+		t.Log(v.AllKeys())
+
+		assert.True(t, v.InConfig("name"))
+		assert.True(t, v.InConfig("clothing.jacket"))
+		assert.False(t, v.InConfig("state"))
+		assert.False(t, v.InConfig("clothing.hat"))
+		assert.Equal(t, "steve", v.Get("name"))
+		assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
+		assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, v.Get("clothing"))
+		assert.Equal(t, 35, v.Get("age"))
+	})
 
-	assert.True(t, v.InConfig("name"))
-	assert.True(t, v.InConfig("clothing.jacket"))
-	assert.False(t, v.InConfig("state"))
-	assert.False(t, v.InConfig("clothing.hat"))
-	assert.Equal(t, "steve", v.Get("name"))
-	assert.Equal(t, []any{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
-	assert.Equal(t, map[string]any{"jacket": "leather", "trousers": "denim", "pants": map[string]any{"size": "large"}}, v.Get("clothing"))
-	assert.Equal(t, 35, v.Get("age"))
+	t.Run("missing config type", func(t *testing.T) {
+		v := New()
+		err := v.ReadConfig(bytes.NewBuffer(yamlExample))
+		require.Error(t, err)
+	})
 }
 
 func TestIsSet(t *testing.T) {
@@ -2127,6 +2136,7 @@ func TestSafeWriteAsConfig(t *testing.T) {
 	v := New()
 	fs := afero.NewMemMapFs()
 	v.SetFs(fs)
+	v.SetConfigType("yaml")
 	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
 	require.NoError(t, err)
 	require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))